proc

package
v0.28.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 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. The rest is per kernel, as ADR 0018's verdict table states it: the reaper's subreaper on Linux, FreeBSD and DragonFly; cgroup as cgroup v2 on Linux, rctl on FreeBSD and a Job Object on Windows; adoption (Attach, AttachPIDFile) on Linux, darwin and the BSDs (ADR 0219); the systemd/notify listener's credential check on Linux and FreeBSD. 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 SandboxValue value type and its parts: the namespaces, mounts, root, capabilities, Landlock rules and seccomp filter a sandboxed child is spawned under (Spec.Sandbox).

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 — non-Unix signal name table (Windows): the portable subset.

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 (
	// NamespaceUser clones the child into a new user namespace (CLONE_NEWUSER).
	NamespaceUser Namespace = 1
	// NamespacePID clones the child into a new PID namespace, as its PID 1 (CLONE_NEWPID).
	NamespacePID Namespace = 2
	// NamespaceMount gives the child a private copy of the mount namespace (CLONE_NEWNS).
	NamespaceMount Namespace = 4
	// NamespaceIPC clones the child into a new IPC namespace (CLONE_NEWIPC).
	NamespaceIPC Namespace = 8
	// NamespaceUTS clones the child into a new UTS namespace (CLONE_NEWUTS).
	NamespaceUTS Namespace = 16
	// NamespaceNet clones the child into a new network namespace, with a loopback only (CLONE_NEWNET).
	NamespaceNet Namespace = 32
	// NamespaceCgroup puts the child in a new cgroup namespace (CLONE_NEWCGROUP),
	// rooted at the group it runs in, Spec.CgroupPath's when one is set — unshared
	// once the child is placed there.
	NamespaceCgroup Namespace = 64
	// NamespaceTime clones the child into a new time namespace (CLONE_NEWTIME, Linux 5.6).
	NamespaceTime Namespace = 128
	// MountReadOnly binds Target onto itself and makes it read-only, recursively (systemd ReadOnlyPaths=) — the zero value.
	MountReadOnly MountKind = 0
	// MountBind binds Source onto Target, recursively (systemd BindPaths=).
	MountBind MountKind = 1
	// MountTmpfs mounts an empty tmpfs on Target, nosuid and nodev (systemd TemporaryFileSystem=).
	MountTmpfs MountKind = 2
	// MountTmpfsSticky mounts an empty tmpfs on Target, mode 1777 — sticky, writable by all, as /tmp is —, nosuid and nodev (systemd PrivateTmp=).
	MountTmpfsSticky MountKind = 3
	// LandlockExecute is LANDLOCK_ACCESS_FS_EXECUTE (ABI 1).
	LandlockExecute LandlockAccess = 1
	// LandlockWriteFile is LANDLOCK_ACCESS_FS_WRITE_FILE (ABI 1).
	LandlockWriteFile LandlockAccess = 2
	// LandlockReadFile is LANDLOCK_ACCESS_FS_READ_FILE (ABI 1).
	LandlockReadFile LandlockAccess = 4
	// LandlockReadDir is LANDLOCK_ACCESS_FS_READ_DIR (ABI 1).
	LandlockReadDir LandlockAccess = 8
	// LandlockRemoveDir is LANDLOCK_ACCESS_FS_REMOVE_DIR (ABI 1).
	LandlockRemoveDir LandlockAccess = 16
	// LandlockRemoveFile is LANDLOCK_ACCESS_FS_REMOVE_FILE (ABI 1).
	LandlockRemoveFile LandlockAccess = 32
	// LandlockMakeChar is LANDLOCK_ACCESS_FS_MAKE_CHAR (ABI 1).
	LandlockMakeChar LandlockAccess = 64
	// LandlockMakeDir is LANDLOCK_ACCESS_FS_MAKE_DIR (ABI 1).
	LandlockMakeDir LandlockAccess = 128
	// LandlockMakeReg is LANDLOCK_ACCESS_FS_MAKE_REG (ABI 1).
	LandlockMakeReg LandlockAccess = 256
	// LandlockMakeSock is LANDLOCK_ACCESS_FS_MAKE_SOCK (ABI 1).
	LandlockMakeSock LandlockAccess = 512
	// LandlockMakeFifo is LANDLOCK_ACCESS_FS_MAKE_FIFO (ABI 1).
	LandlockMakeFifo LandlockAccess = 1024
	// LandlockMakeBlock is LANDLOCK_ACCESS_FS_MAKE_BLOCK (ABI 1).
	LandlockMakeBlock LandlockAccess = 2048
	// LandlockMakeSym is LANDLOCK_ACCESS_FS_MAKE_SYM (ABI 1).
	LandlockMakeSym LandlockAccess = 4096
	// LandlockRefer is LANDLOCK_ACCESS_FS_REFER (ABI 2, Linux 5.19).
	LandlockRefer LandlockAccess = 8192
	// LandlockTruncate is LANDLOCK_ACCESS_FS_TRUNCATE (ABI 3, Linux 6.2).
	LandlockTruncate LandlockAccess = 16384
	// LandlockIoctlDev is LANDLOCK_ACCESS_FS_IOCTL_DEV (ABI 5, Linux 6.10).
	LandlockIoctlDev LandlockAccess = 32768
	// SeccompKill kills the whole process (SECCOMP_RET_KILL_PROCESS, Linux 4.14) — the zero value.
	SeccompKill SeccompAction = 0
	// SeccompErrno fails the call with SeccompValue.Errno (SECCOMP_RET_ERRNO).
	SeccompErrno SeccompAction = 1
	// SeccompAllow lets the call through (SECCOMP_RET_ALLOW).
	SeccompAllow SeccompAction = 2
)
View Source
const (
	// SchedOther is the normal time-sharing policy (SCHED_OTHER) — the zero value.
	SchedOther SchedPolicy = 0
	// SchedFIFO is the first-in first-out realtime policy (SCHED_FIFO).
	SchedFIFO SchedPolicy = 1
	// SchedRR is the round-robin realtime policy (SCHED_RR).
	SchedRR SchedPolicy = 2
	// SchedBatch is the policy for CPU-bound batch work (SCHED_BATCH).
	SchedBatch SchedPolicy = 3
	// SchedIdle runs the child only when nothing else wants the CPU (SCHED_IDLE).
	SchedIdle SchedPolicy = 5
	// IOClassNone sets no I/O class, the kernel deriving one from the CPU niceness — the zero value.
	IOClassNone IOClass = 0
	// IOClassRealTime is the realtime I/O class (IOPRIO_CLASS_RT).
	IOClassRealTime IOClass = 1
	// IOClassBestEffort is the best-effort I/O class (IOPRIO_CLASS_BE).
	IOClassBestEffort IOClass = 2
	// IOClassIdle gets I/O only when no other process wants the disk (IOPRIO_CLASS_IDLE).
	IOClassIdle IOClass = 3
)
View Source
const CodeAdoptionRefused errs.Code = 0x00_02_06_21 // 0.2.6.33

CodeAdoptionRefused identifies a process Attach or AttachPIDFile may not adopt: PID 1, this process, one outside the cgroup or the users asked for, or one named by a PID file nobody but root or this process's user vouches for and with no bound given.

View Source
const CodeCgroupControllerAbsent errs.Code = 0x00_02_06_25 // 0.2.6.37

CodeCgroupControllerAbsent identifies a cgroup v2 controller asked of a group whose parent does not offer it: absent from the root's cgroup.controllers, or refused by the kernel when it was enabled.

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 CodeCgroupReadFailed errs.Code = 0x00_02_06_1D // 0.2.6.29

CodeCgroupReadFailed identifies a control group whose counters could not be read: its own files are gone (the group was deleted) or unreadable.

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 CodeHandleUnavailable errs.Code = 0x00_02_06_19 // 0.2.6.25

CodeHandleUnavailable identifies a ProcessHandle operation on a process whose race-free kernel handle — a pidfd on Linux, a process HANDLE on Windows — could not be obtained: a kernel without pidfd, or a filter that refuses it.

View Source
const CodeInvalidFDName errs.Code = 0x00_02_06_23 // 0.2.6.35

CodeInvalidFDName identifies a descriptor name LISTEN_FDNAMES cannot carry: longer than systemd's FDNAME_MAX, or a byte that is not printable ASCII, or a ':'.

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 CodeMemlimitMisconfigured errs.Code = 0x00_02_06_1C // 0.2.6.28

CodeMemlimitMisconfigured identifies a soft-memory-limit configuration memlimit cannot honour: a headroom outside (0, 100] percent, or a negative floor.

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 CodePIDFileRefused errs.Code = 0x00_02_06_20 // 0.2.6.32

CodePIDFileRefused identifies a PID file AttachPIDFile could not read safely: a relative path, a symbolic link at its end, a component that is no directory, a file that is not regular, or content that is not one PID.

View Source
const CodePTYFailed errs.Code = 0x00_02_06_1A // 0.2.6.26

CodePTYFailed identifies a pseudo-terminal that could not be allocated, unlocked, opened or resized for a StdioPTY spawn, or a Terminal operation on a child that has none.

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 CodeReservedEnv errs.Code = 0x00_02_06_1F // 0.2.6.31

CodeReservedEnv identifies a Spec whose Env names a variable of the SDK's own spawn protocol — a key beginning __KITSUNIUM_SDK_PROC_, in any case — through which a caller could otherwise override what the spawn tells its child.

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 CodeSandboxFailed errs.Code = 0x00_02_06_1E // 0.2.6.30

CodeSandboxFailed identifies a step of Spec.Sandbox that failed in the child before its program ran — a mount, the pivot, a capability, the identity, Landlock, the seccomp filter —: the program never ran.

View Source
const CodeSandboxUnavailable errs.Code = 0x00_02_06_24 // 0.2.6.36

CodeSandboxUnavailable identifies a part of Spec.Sandbox the running kernel cannot apply — Landlock or one of its rights, seccomp, a namespace, procfs, memfd_create, mount_setattr —, refused before the fork.

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 CodeStatusUnknown errs.Code = 0x00_02_06_22 // 0.2.6.34

CodeStatusUnknown identifies the end of an attached process: it was not this process's child, so it is gone with an exit status nobody here can read.

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 CodeStdioDrainTimeout errs.Code = 0x00_02_06_18 // 0.2.6.24

CodeStdioDrainTimeout identifies a Wait whose StdioCapture copiers had not reached EOF when Spec.DrainGrace closed after the exit: a process the child left behind still held its output pipe. The ExitValue is the real one.

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.Static(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",
		0,
		69)

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

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

	// UnknownGroup is returned when a Spec names a Group or supplementary group
	// that does not resolve to a gid on the host.
	UnknownGroup = errs.Static(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",
		0,
		67)

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

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

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

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

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

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

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

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

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

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

	// CgroupUnavailable is returned when a cgroup operation runs where cgroup v2
	// is not mounted or not delegated to the caller.
	CgroupUnavailable = errs.Static(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",
		0,
		69)

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

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

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

	// 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.Static(CodeNotifyFailed, "NOTIFY_FAILED",
		"Could not send the sd_notify datagram",
		"service/proc/systemd/notify: writing to $NOTIFY_SOCKET failed",
		0,
		71)

	// ListenFailed is returned when the supervisor-side sd_notify datagram socket
	// cannot be created or bound.
	ListenFailed = errs.Static(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",
		0,
		71)

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

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

	// 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.Static(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",
		0,
		74)

	// StdioDrainTimeout is returned from Wait, beside the real ExitValue, when the
	// StdioCapture copiers had not reached EOF when Spec.DrainGrace closed after
	// the exit: something the child left behind still held its output pipe. The
	// parent's pipe ends are closed, and the fields name the streams held and the
	// bytes delivered.
	StdioDrainTimeout = errs.Static(CodeStdioDrainTimeout, "STDIO_DRAIN_TIMEOUT",
		"The process exited but its output was still held open",
		"service/proc/exec: the capture pipes were still held open when the drain grace closed after the child's exit",
		0,
		74)

	// HandleUnavailable is returned by a ProcessHandle operation, or by Open, when no
	// race-free kernel handle exists for the process: a kernel without pidfd, or a
	// filter that refuses it.
	HandleUnavailable = errs.Static(CodeHandleUnavailable, "HANDLE_UNAVAILABLE",
		"No process handle is available",
		"service/proc/exec: no pidfd or process HANDLE could be obtained for the process",
		0,
		69)

	// PTYFailed is returned when a pseudo-terminal cannot be allocated, unlocked,
	// opened or resized for a StdioPTY spawn, or when a Terminal operation targets a
	// child started without one.
	PTYFailed = errs.Static(CodePTYFailed, "PTY_FAILED",
		"Could not set up the pseudo-terminal",
		"service/proc/exec: allocating, opening or resizing the pseudo-terminal failed",
		0,
		71)

	// MemlimitMisconfigured is returned by memlimit's ApplyWith and Watch when the
	// configuration cannot be honoured: a headroom outside (0, 100] percent, or a
	// negative floor. Nothing is installed (ADR 0031).
	MemlimitMisconfigured = errs.Static(CodeMemlimitMisconfigured, "MEMLIMIT_MISCONFIGURED",
		"The memory limit configuration is invalid",
		"service/proc/memlimit: ConfigValue.HeadroomPercent outside [0, 100] or MinimumLimit negative",
		0,
		64)

	// CgroupReadFailed is returned by GroupReader.Stat and Members when the
	// group's own counters cannot be read: the group was deleted, or its files
	// are unreadable to the caller.
	CgroupReadFailed = errs.Static(CodeCgroupReadFailed, "CGROUP_READ_FAILED",
		"Could not read the control group's counters",
		"service/proc/cgroup: reading the group's cpu.stat, cgroup.procs or job accounting failed",
		0,
		74)

	// SandboxFailed is returned by Start when a step of Spec.Sandbox failed in the
	// child before its program ran: the step field names it — mounts, pivot_root,
	// landlock_paths, capbset_drop, securebits, identity, capabilities,
	// cloexec_sweep, no_new_privs, landlock_restrict_self, seccomp — and the errno
	// field gives the kernel's answer. The program never ran.
	SandboxFailed = errs.Static(CodeSandboxFailed, "SANDBOX_FAILED",
		"Could not confine the process",
		"service/proc/exec.Start: a step of Spec.Sandbox failed in the child before its program ran; the step field names it",
		0,
		71)

	// ReservedEnv is returned by Start and Run, before any fork, when Spec.Env names
	// a variable of the SDK's own spawn protocol: a key that begins
	// __KITSUNIUM_SDK_PROC_, compared without regard to case. The spawn sets those
	// variables itself, and a child reads the first of two equal keys, so one in
	// Spec.Env would override what the SDK tells the child.
	ReservedEnv = errs.Static(CodeReservedEnv, "RESERVED_ENV",
		"The environment names a variable reserved by the SDK",
		"service/proc/exec.Start: Spec.Env names a reserved __KITSUNIUM_SDK_PROC_ variable",
		0,
		64)

	// PIDFileRefused is returned by AttachPIDFile for a PID file it cannot read
	// safely: a relative path, a symbolic link at its end, a component that is no
	// directory, a file that is not regular, one it cannot open — the cause, such as
	// fs.ErrNotExist, is wrapped — or content that is not one decimal PID.
	PIDFileRefused = errs.Static(CodePIDFileRefused, "PID_FILE_REFUSED",
		"The PID file cannot be read safely",
		"service/proc/exec.AttachPIDFile: the PID file is not a regular file reached safely, or holds no single PID; the why field says which",
		0,
		65)

	// AdoptionRefused is returned by Attach and AttachPIDFile for a process they may
	// not adopt: PID 1 or this process, one outside the cgroup or the users the
	// AttachValue asks for, or — from a PID file another user could have written —
	// any process when the AttachValue bounds nothing.
	AdoptionRefused = errs.Static(CodeAdoptionRefused, "ADOPTION_REFUSED",
		"The process may not be adopted",
		"service/proc/exec.Attach: the process is PID 1, this process, outside the cgroup or users asked for, or named by an untrusted PID file with no bound",
		0,
		77)

	// StatusUnknown is what Wait returns, with an ExitValue whose Code is -1, once a
	// process Attach or AttachPIDFile adopted has ended: it was not this process's
	// child, so its exit status is not this process's to read.
	StatusUnknown = errs.Static(CodeStatusUnknown, "STATUS_UNKNOWN",
		"The process ended with an unknown status",
		"service/proc/exec: an attached process is not this process's child; it ended and its exit status cannot be read",
		0,
		0)

	// InvalidFDName is returned by systemd/listen's CheckName, Prepare and
	// PrepareFiles for a descriptor name LISTEN_FDNAMES cannot carry: longer than
	// systemd's FDNAME_MAX, a byte that is not printable ASCII, or a ':' — the
	// list's separator (systemd's fdname_is_valid).
	InvalidFDName = errs.Static(CodeInvalidFDName, "INVALID_FD_NAME",
		"The descriptor name is not valid",
		"service/proc/systemd/listen: a descriptor name is longer than FDNAME_MAX, not printable ASCII, or holds a ':'",
		0,
		64)

	// SandboxUnavailable is returned by Start, before any fork, for a part of
	// Spec.Sandbox the running kernel lacks, and by LandlockABI on a kernel without
	// Landlock: the feature field names the function — landlock, or
	// landlock:<right> beside the abi field, seccomp, namespace:<kind>, procfs,
	// memfd_create, mount_setattr, cap_last_cap or capability.
	SandboxUnavailable = errs.Static(CodeSandboxUnavailable, "SANDBOX_UNAVAILABLE",
		"The kernel cannot apply the requested confinement",
		"service/proc/exec.Start: this kernel lacks a part of Spec.Sandbox; the feature field names it",
		0,
		69)

	// CgroupControllerAbsent is returned when a cgroup v2 controller is asked
	// of a group whose parent does not offer it: Create's WithControllers and
	// GroupTree.EnableControllers name the controller and the directory.
	CgroupControllerAbsent = errs.Static(CodeCgroupControllerAbsent, "CGROUP_CONTROLLER_ABSENT",
		"The cgroup controller is not available here",
		"service/proc/cgroup: the controller is absent from cgroup.controllers or refused by cgroup.subtree_control",
		0,
		69)
)

Functions

This section is empty.

Types

type AttachValue added in v0.28.0

type AttachValue struct {
	// Cgroup is a cgroup v2 path as /proc/<pid>/cgroup names it —
	// "/system.slice/web.service" —: the process must be in it or below it. Empty
	// checks no cgroup.
	Cgroup string
	// UIDs are the users the process may run as: its real and its effective uid
	// must both be one of them. Empty checks no user.
	UIDs []int
}

AttachValue bounds the process a PID file may name when the file is not trustworthy — reached through a symbolic link another user could have made, or kept in a directory, or written to a file, that another user could change: the process must then lie in the cgroup v2 subtree Cgroup and run as one of UIDs. A bound that is given is checked whatever the file; the zero value bounds nothing, and a file that is not trustworthy is then refused.

type CapabilitiesValue added in v0.28.0

type CapabilitiesValue struct {
	// Bounding is the bounding set the child keeps (systemd
	// CapabilityBoundingSet=): every capability the kernel knows outside it is
	// dropped with PR_CAPBSET_DROP, which needs CAP_SETPCAP — as uid 0 in a user
	// namespace has. The zero value drops them all.
	Bounding uint64
	// Ambient is raised in the child's ambient set (systemd
	// AmbientCapabilities=), so a program that is not root keeps them across
	// execve; its inheritable set becomes this one. It must lie within Bounding.
	Ambient uint64
	// SecureBits, when non-zero, is set with PR_SET_SECUREBITS (systemd
	// SecureBits=), its bits those of linux/securebits.h.
	SecureBits uint32
}

CapabilitiesValue is the capability state a sandboxed child execs with (SandboxValue.Capabilities). A mask's bit n is capability n (linux/capability.h: 0 CAP_CHOWN … 40 CAP_CHECKPOINT_RESTORE).

type ContextWaiter added in v0.28.0

type ContextWaiter interface {
	// WaitContext waits like Wait until the process exits or ctx is done. When
	// ctx ends first it returns ctx's error and the child keeps running; a later
	// Wait or WaitContext still reports the real exit.
	WaitContext(ctx context.Context) (ExitValue, error)
}

ContextWaiter is the sibling of Process (ADR 0039) that bounds a wait: the handle Start returns implements it, a Process from elsewhere may not.

type Controller added in v0.28.0

type Controller uint8

Controller is a cgroup v2 controller: String is its name as the kernel spells it in cgroup.controllers and cgroup.subtree_control. The constants are the controllers of Linux 6.12; the zero value is none and is not Known.

const (
	// ControllerCPU distributes CPU time: cpu.weight, cpu.max, cpu.max.burst.
	ControllerCPU Controller = iota + 1
	// ControllerCPUSet pins tasks to CPUs and memory nodes: cpuset.cpus,
	// cpuset.mems.
	ControllerCPUSet
	// ControllerIO distributes block I/O: io.weight, io.max, io.latency.
	ControllerIO
	// ControllerMemory bounds memory: memory.min/low/high/max, the swap and
	// zswap limits, memory.oom.group.
	ControllerMemory
	// ControllerPids bounds the number of tasks: pids.max.
	ControllerPids
	// ControllerHugeTLB bounds huge-page use: hugetlb.<size>.max.
	ControllerHugeTLB
	// ControllerRDMA bounds RDMA resources: rdma.max.
	ControllerRDMA
	// ControllerMisc bounds scalar resources the kernel lists: misc.max.
	ControllerMisc
)

func (Controller) Known added in v0.28.0

func (c Controller) Known() bool

Known reports whether c is one of the Controller constants.

func (Controller) String added in v0.28.0

func (c Controller) String() string

String returns the controller's kernel name, "memory", or "controller(N)" for a value no constant holds.

type DescriptorsValue added in v0.28.0

type DescriptorsValue struct {
	// Files are the descriptors the datagram carried (SCM_RIGHTS): with
	// FDSTORE=1 the ones to store, with BARRIER=1 the barrier, which the caller
	// closes once every earlier datagram is processed.
	Files []*os.File
	// MainPIDFD is the pidfd a MAINPIDFD=1 datagram carried: its process is the
	// new main process, and MainPID its PID; nil when none.
	MainPIDFD *os.File
	// SenderPIDFD is a pidfd on the sending process (SCM_PIDFD, Linux 6.5 and
	// later): the sender itself, beyond the reach of a PID reuse; nil where the
	// kernel attaches none.
	SenderPIDFD *os.File
}

DescriptorsValue are the descriptors one sd_notify datagram brought (NotificationValue.Descriptors): the caller owns every one and closes those it does not keep. Descriptors that came with no assignment calling for them are closed by the Listener, as systemd does.

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 KiB on every platform, read from
	// wait4's rusage: darwin and ios report bytes, which are converted to KiB
	// rounding up; Linux and the BSDs report KiB, passed through. It is 0 when
	// the platform does not supply it (illumos, Solaris and Windows).
	MaxRSS int64
	// CoreDumped reports whether the signalled death left a core image
	// (WaitStatus.CoreDump). It is false for a normal exit, and wherever the
	// platform cannot say (Windows).
	CoreDumped bool
	// Usage is the rest of wait4's resource accounting — page faults, block I/O
	// and context switches — read from the same rusage as UserTime and MaxRSS.
	// It is nil where the platform does not supply it (illumos, Solaris and
	// Windows).
	Usage *UsageValue
}

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 FDPolicy added in v0.28.0

type FDPolicy uint8

FDPolicy says which of the supervisor's descriptors a child inherits besides its stdio and ExtraFiles (Spec.FDs). The zero value FDsSealed gives it none.

const (
	// FDsSealed gives the child no descriptor of the supervisor's past its stdio and ExtraFiles — the zero value.
	FDsSealed FDPolicy = 0
	// FDsInherited lets the child inherit every descriptor of the supervisor that lacks close-on-exec.
	FDsInherited FDPolicy = 1
)

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 a group nothing will be spawned into again: on Linux (measured on
	// 6.12), after one cgroup.kill — an empty group's too — every child later
	// cloned into the group (Spec.CgroupPath, CLONE_INTO_CGROUP) is SIGKILLed at
	// its fork, while a process moved in by Add is not. To empty a group that
	// will be reused, signal its members instead (GroupSignaler's Signal).
	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 GroupEventsValue added in v0.28.0

type GroupEventsValue struct {
	// MemoryEvents counts what the group's memory limits did — memory.events,
	// zero where the memory controller is not enabled for the group.
	MemoryEvents MemoryEventsValue
	// Populated reports that the group or a group beneath it holds a live
	// process (cgroup.events "populated").
	Populated bool
	// Frozen reports that the group is frozen (cgroup.events "frozen").
	Frozen bool
}

GroupEventsValue is a control group's state as GroupWatcher.Watch reads it after each change: its cgroup.events keys and its memory.events counters.

type GroupReader added in v0.28.0

type GroupReader interface {
	// Stat reads the group's counters: CPU time and throttling, memory now, at
	// its peak and the events of its limits, the pid count, the I/O per device.
	// It returns CgroupReadFailed when the group's own files cannot be read —
	// the group was deleted.
	Stat() (GroupStatValue, error)
	// Members lists the pids of the group's processes and of the groups
	// beneath it — the cgroup.procs of the subtree, the tracked set, the
	// job's process list — in no promised order: a process a delegated
	// service moved into a group of its own is still the group's.
	Members() ([]int, error)
}

GroupReader is the read half a control group may offer beside Group (ADR 0039, asserted on the Group cgroup.Create returns): what the confined tree has used. A backend reads what its kernel counts and says what it cannot with GroupStatValue.Partial; a platform with no control group has no GroupReader.

type GroupSignaler added in v0.28.0

type GroupSignaler interface {
	// Signal sends sig to every process of the group and of the groups beneath
	// it but the caller's own, reading cgroup.procs again until a pass finds no
	// process it has not signalled, at most 64 passes. A process gone before the
	// signal reached it is not an error; any other refusal is SignalFailed.
	Signal(sig Signal) error
	// Contains reports whether pid is a process of the group or of a group
	// beneath it. CgroupReadFailed when the group's cgroup.procs cannot be read.
	Contains(pid int) (bool, error)
}

GroupSignaler is the sibling of Group (ADR 0039, asserted on the Group cgroup.Create returns on Linux) that reaches every process of the group and of the groups beneath it — systemd's KillMode=control-group and mixed. With Group's Kill it is what proc/service's CgroupKill asks of a unit's group.

type GroupStatValue added in v0.28.0

type GroupStatValue struct {
	// CPUUsage is the CPU time the group's tasks used, user and system.
	CPUUsage time.Duration
	// CPUUser is the user-mode share of CPUUsage.
	CPUUser time.Duration
	// CPUSystem is the kernel-mode share of CPUUsage.
	CPUSystem time.Duration
	// ThrottledTime is how long cpu.max held the group's tasks back.
	ThrottledTime time.Duration
	// NrThrottled is how many periods cpu.max throttled the group in.
	NrThrottled int64
	// MemoryCurrent is the memory the group holds now, in bytes.
	MemoryCurrent int64
	// MemoryPeak is the most memory the group held, in bytes (Linux 5.19's
	// memory.peak; Windows' peak job memory).
	MemoryPeak int64
	// MemoryEvents counts what the group's memory limits did.
	MemoryEvents MemoryEventsValue
	// PidsCurrent is how many tasks the group holds now.
	PidsCurrent int64
	// IO is the I/O the group did, one entry per device.
	IO []IOStatValue
	// Partial reports that at least one counter could not be read and is zero.
	Partial bool
}

GroupStatValue is what a control group's tree has used, as GroupReader.Stat read it. A counter the backend cannot read is zero, and Partial says that some are: memory.peak before Linux 5.19, a controller not enabled for the group, and on Windows and FreeBSD everything their kernels do not count.

type GroupTree added in v0.28.0

type GroupTree interface {
	// Path is the group's directory: the CgroupPath a Spec spawns a child straight into.
	Path() string
	// EnableControllers writes ctrls to the group's cgroup.subtree_control, so
	// the groups beneath it get their interface files. A controller the group's
	// cgroup.controllers does not list is CgroupControllerAbsent, and a
	// malformed name InvalidSpec, before anything is written; while the group
	// itself holds a process the kernel refuses with EBUSY, CgroupWriteFailed
	// (cgroup v2's no-internal-process rule).
	EnableControllers(ctrls ...Controller) error
	// Delegate hands the group to the user uid and the group gid (systemd
	// Delegate=yes): it changes the owner of the directory and of the files the
	// kernel lists in /sys/kernel/cgroup/delegate — cgroup.procs,
	// cgroup.threads and cgroup.subtree_control where that list is absent — so
	// that user may create groups beneath it, move its processes and enable
	// controllers. -1 keeps an owner unchanged. A file the group lacks is
	// skipped; a refused change is CgroupWriteFailed.
	Delegate(uid, gid int) error
	// DeleteTree removes the group and every group beneath it, deepest first:
	// what a delegated unit made under its group goes with it, as systemd
	// cleans a Delegate=yes unit up. A group still holding a process stops it
	// with CgroupDeleteFailed naming that group — Kill, and wait for Watch to
	// say the group is empty, first. A link is never followed.
	DeleteTree() error
}

GroupTree is the sibling of Group (ADR 0039, asserted on the Group cgroup.Create returns on Linux) for the group as a node of the hierarchy: its directory, the controllers the groups beneath it get, its delegation to another user, and its removal with what was made beneath it.

type GroupTuner added in v0.28.0

type GroupTuner interface {
	// SetMemoryMin writes memory.min (systemd MemoryMin=), the memory the group
	// keeps whatever the pressure; a negative value writes "max".
	SetMemoryMin(bytes int64) error
	// SetMemoryLow writes memory.low (systemd MemoryLow=), the memory reclaimed
	// from the group only when nothing else can be; a negative value writes
	// "max".
	SetMemoryLow(bytes int64) error
	// SetMemoryHigh writes memory.high (systemd MemoryHigh=), the throttle
	// above which the group's allocations are slowed and reclaimed; a negative
	// value writes "max".
	SetMemoryHigh(bytes int64) error
	// SetMemorySwapMax writes memory.swap.max (systemd MemorySwapMax=); a
	// negative value writes "max".
	SetMemorySwapMax(bytes int64) error
	// SetMemoryZswapMax writes memory.zswap.max (systemd MemoryZSwapMax=); a
	// negative value writes "max".
	SetMemoryZswapMax(bytes int64) error
	// SetMemoryZswapWriteback writes memory.zswap.writeback (systemd
	// MemoryZSwapWriteback=, Linux 6.8): false keeps the group's compressed
	// pages from being written back to swap.
	SetMemoryZswapWriteback(enabled bool) error
	// SetMemoryOOMGroup writes memory.oom.group: true makes the OOM killer end
	// the whole group rather than one task of it (systemd OOMPolicy=kill).
	SetMemoryOOMGroup(enabled bool) error
	// SetCPUWeight writes cpu.weight (systemd CPUWeight=), the group's share of
	// CPU against its siblings, 1 to 10000 (100 by default).
	SetCPUWeight(weight int64) error
	// SetCPUMaxBurst writes cpu.max.burst, the CPU time the group may borrow
	// above cpu.max's quota in a period, in microseconds; a negative burst is
	// InvalidSpec.
	SetCPUMaxBurst(burst time.Duration) error
	// SetCPUSetCPUs writes cpuset.cpus (systemd AllowedCPUs=) in the kernel's
	// list form, "0-3,8"; an empty list writes "", the parent's CPUs. A
	// negative CPU is InvalidSpec.
	SetCPUSetCPUs(cpus []int) error
	// SetCPUSetMems writes cpuset.mems (systemd AllowedMemoryNodes=) in the
	// list form SetCPUSetCPUs writes.
	SetCPUSetMems(nodes []int) error
	// SetIOWeight writes io.weight's default (systemd IOWeight=), the group's
	// share of I/O against its siblings, 1 to 10000 (100 by default).
	SetIOWeight(weight int64) error
	// SetIOLatency writes the io.latency line of the device major:minor
	// (systemd IODeviceLatencyTargetSec=): the latency the group's I/O is
	// protected to, in microseconds; zero removes it, and a negative target is
	// InvalidSpec.
	SetIOLatency(major, minor uint32, target time.Duration) error
}

GroupTuner is the sibling of Group (ADR 0039, asserted on the Group cgroup.Create returns on Linux) that writes the cgroup v2 interface files Group does not: the memory protections and throttle, the swap and zswap limits, the OOM grouping, the CPU weight and burst, the cpusets, the I/O weight and latency. Each setter writes one file of the group, which exists only when the parent enables its controller (WithControllers, GroupTree.EnableControllers): a write to an absent file is CgroupWriteFailed. A value outside the kernel's range is InvalidSpec, and nothing is written.

type GroupWatcher added in v0.28.0

type GroupWatcher interface {
	// Watch returns a channel holding the group's state first, then its state
	// after each change the kernel announces: populated, frozen, the memory
	// events. The channel holds one value, and a burst coalesces into the
	// latest — published even when the burst left the state as it was, so a
	// reader that read the group meanwhile learns it changed back: the watch
	// never blocks on its reader. It is closed when ctx ends, and when the
	// group is gone, after a last value holding no process; a reader whose ctx
	// is not done reads the close as the group's removal. CgroupReadFailed
	// when the group's cgroup.events cannot be watched or read.
	Watch(ctx context.Context) (<-chan GroupEventsValue, error)
}

GroupWatcher is the sibling of Group (ADR 0039, asserted on the Group cgroup.Create returns on Linux) that follows the group's state: cgroup.events and memory.events, read again after every change the kernel announces with inotify.

type IDMapValue added in v0.28.0

type IDMapValue struct {
	// Inside is the first id in the namespace.
	Inside uint32
	// Outside is the id Inside maps to in the supervisor's namespace.
	Outside uint32
	// Size is the number of ids mapped; it must be at least 1.
	Size uint32
}

IDMapValue is one line of a user namespace's uid_map or gid_map: Size ids from Inside in the namespace are Outside and up in the supervisor's.

type IOClass added in v0.28.0

type IOClass int

IOClass is an I/O scheduling class (Spec.Scheduling). Its values are Linux's IOPRIO_CLASS_* numbers; the zero value IOClassNone sets none.

func (IOClass) String added in v0.28.0

func (c IOClass) String() string

String returns the lowercase class name ("none", "realtime", "besteffort", "idle"), or "ioclass(N)" for an undefined value.

type IOStatValue added in v0.28.0

type IOStatValue struct {
	// Major is the device's major number.
	Major uint32
	// Minor is the device's minor number.
	Minor uint32
	// RBytes is the bytes read.
	RBytes uint64
	// WBytes is the bytes written.
	WBytes uint64
	// RIOs is the read operations.
	RIOs uint64
	// WIOs is the write operations.
	WIOs uint64
}

IOStatValue is one device's I/O by a control group — a line of Linux's io.stat.

type JobLimiter added in v0.28.0

type JobLimiter interface {
	// SetJobMemoryLimit caps the memory committed by every process of the job
	// together (JOB_OBJECT_LIMIT_JOB_MEMORY); a negative value removes the cap.
	// CgroupWriteFailed when the job refuses it.
	SetJobMemoryLimit(bytes int64) error
}

JobLimiter is the sibling of Group (ADR 0039) on Windows, where Group's SetMemoryMax caps each process of the Job Object: it caps the job as a whole. Group's SetMemoryMax is unchanged.

type LandlockAccess added in v0.28.0

type LandlockAccess uint64

LandlockAccess is a set of Landlock filesystem access rights, one bit per right, numbered as LANDLOCK_ACCESS_FS_* (linux/landlock.h).

type LandlockRuleValue added in v0.28.0

type LandlockRuleValue struct {
	// Path is the absolute file or directory the rule covers, opened by the
	// child after its mounts and its pivot.
	Path string
	// Access is the set of rights granted beneath Path; it must lie within
	// LandlockValue.Handled.
	Access LandlockAccess
}

LandlockRuleValue grants Access beneath Path (LANDLOCK_RULE_PATH_BENEATH): to the file or directory itself and to everything below it.

type LandlockValue added in v0.28.0

type LandlockValue struct {
	// Handled is the set of rights restricted; it must not be empty.
	Handled LandlockAccess
	// Rules grant rights beneath paths.
	Rules []LandlockRuleValue
	// BestEffort is the set of rights dropped rather than refused when the
	// kernel's ABI lacks them.
	BestEffort LandlockAccess
}

LandlockValue restricts a child's filesystem access (SandboxValue.Landlock): every right in Handled is denied beneath every path but the Rules' and the rights they grant there; a right outside Handled is not restricted. The kernel's Landlock ABI decides which rights exist: one it lacks is refused with SandboxUnavailable unless BestEffort names it, in which case it is left unrestricted. The ABI in effect is LandlockABI's answer.

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 MemoryBound added in v0.28.0

type MemoryBound uint8

MemoryBound names which control-group file bound a soft-memory-limit derivation, so a log line says whether the throttle (memory.high) or the hard cap (memory.max) set the allowance.

The zero value is deliberately unclaimed, as MemorySource's is: it is the Bound of every derivation that read no allowance.

const (
	// MemoryBoundHigh reports that a memory.high file bound the derivation: the
	// throttle above which the kernel reclaims and slows the group's allocations,
	// long before memory.max would kill it.
	//
	// The run starts at iota+1 so the zero stays unminted — see the type comment.
	MemoryBoundHigh MemoryBound = iota + 1
	// MemoryBoundMax reports that a hard cap bound the derivation: a cgroup v2
	// memory.max file, or cgroup v1's memory.limit_in_bytes — v1 has no throttle
	// to read beside it.
	MemoryBoundMax
)

func (MemoryBound) String added in v0.28.0

func (b MemoryBound) String() string

String renders the bound for a log line: "high", "max", or "unknown" for a value this package never mints, the zero included.

type MemoryEventsValue added in v0.28.0

type MemoryEventsValue struct {
	// Low counts reclaims under memory.low's protection.
	Low int64
	// High counts the times the group was throttled above memory.high.
	High int64
	// Max counts the times the group hit memory.max.
	Max int64
	// OOM counts the times the group ran out of memory.
	OOM int64
	// OOMKill counts the tasks the OOM killer ended in the group.
	OOMKill int64
}

MemoryEventsValue counts what a control group's memory limits did — Linux's memory.events.

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
	// Bound names the file that set Allowance — memory.high or the hard cap —
	// on every outcome that carries an Allowance, and is zero otherwise.
	Bound MemoryBound
}

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 MountKind added in v0.28.0

type MountKind uint8

MountKind is what a MountValue mounts. The zero value MountReadOnly makes an existing tree read-only.

type MountValue added in v0.28.0

type MountValue struct {
	// Kind is what is mounted.
	Kind MountKind
	// Source is the directory or file a MountBind binds; empty for every other
	// kind.
	Source string
	// Target is the absolute path mounted on; it must exist.
	Target string
	// ReadOnly makes a MountBind or a MountTmpfs read-only, recursively (a
	// MountReadOnly always is).
	ReadOnly bool
}

MountValue is one mount the child makes in its mount namespace (SandboxValue.Mounts).

type Namespace added in v0.28.0

type Namespace uint32

Namespace is a set of Linux namespaces (SandboxValue.Namespaces), one bit per namespace. The zero value is no namespace.

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
	// SenderUID is the sending process's user, as verified by the kernel with
	// SenderPID; it means nothing when SenderPID is 0.
	SenderUID int
	// SenderGID is the sending process's group, as verified by the kernel with
	// SenderPID; it means nothing when SenderPID is 0.
	SenderGID int
	// Descriptors are the descriptors the datagram brought — the ones it carried,
	// a pidfd on its sender —, which the caller owns; nil when it brought none.
	Descriptors *DescriptorsValue
}

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 PTYValue added in v0.28.0

type PTYValue struct {
	// Rows is the number of character rows.
	Rows uint16
	// Cols is the number of character columns.
	Cols uint16
}

PTYValue is a pseudo-terminal's window size in character cells, as TIOCSWINSZ sets it and TIOCGWINSZ reads it.

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. A process the child
	// left behind can hold its output pipe past the exit: Spec.DrainGrace bounds
	// that wait, after which the pipes are closed and the error, beside the real
	// ExitValue, is StdioDrainTimeout.
	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.
	// On Windows a Setpgid child's console process group receives the matching
	// console control event instead — CTRL_C for SIGINT, CTRL_BREAK for SIGTERM,
	// SIGHUP and SIGQUIT — and SIGKILL, or any signal to a child without its own
	// group, reaches the leader alone.
	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. On Windows sig reaches a Setpgid child as its
	// console control event, as SignalGroup sends it, and the escalation is
	// TerminateProcess.
	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 ProcessHandle added in v0.28.0

type ProcessHandle interface {
	// Handle returns a new descriptor for the process, which the caller owns and
	// closes: a pidfd on Linux — readable once the process exits, so it can be
	// polled, or passed to another process — and a duplicated process HANDLE on
	// Windows. It reports false where none is available: a kernel without pidfd,
	// another platform, or a process already reaped.
	Handle() (*os.File, bool)
	// SignalHandle delivers sig through the process's kernel handle
	// (pidfd_send_signal on Linux), never by pid: once the process is reaped it
	// fails with SignalFailed wrapping ESRCH instead of reaching a process that
	// reused the pid. It returns HandleUnavailable where the kernel gave no handle,
	// and UnsupportedPlatform off Linux and Windows.
	SignalHandle(sig Signal) error
}

ProcessHandle is the sibling of Process (ADR 0039) that reaches the process through a kernel handle rather than its pid, which another process may hold once this one is reaped: a pidfd on Linux 5.4 and later, the process HANDLE on Windows. The handles Start and Open return implement it.

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=. A child spawned without it gets the
	// supervisor's ORIGINAL soft limit, not the one the Go runtime raised at start,
	// unless rlimit.KeepRaisedNoFile was called.
	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 ResultValue added in v0.28.0

type ResultValue struct {
	// Exit is the child's ExitValue.
	Exit ExitValue
	// Stdout is the first RunValue.MaxOutputBytes bytes the child wrote to its standard output.
	Stdout []byte
	// Stderr is the first RunValue.MaxOutputBytes bytes the child wrote to its standard error.
	Stderr []byte
	// Truncated reports whether either stream wrote more than MaxOutputBytes, the
	// rest of it read and discarded.
	Truncated bool
}

ResultValue is what Run returns: the child's exit and the head of each of its output streams.

type RunValue added in v0.28.0

type RunValue struct {
	// MaxOutputBytes is the number of bytes kept from each of stdout and stderr.
	// The child's output past it is read and discarded, so the child never blocks
	// on a full pipe and the caller never holds more. It must be positive: Run
	// refuses zero with InvalidSpec (ADR 0031).
	MaxOutputBytes int
	// Grace is how long a child whose run was cancelled is given between SIGTERM
	// and SIGKILL; zero or less escalates at once.
	Grace time.Duration
}

RunValue configures Run: how much of each output stream it keeps, and how long a cancelled run's child is given to stop.

type SandboxValue added in v0.28.0

type SandboxValue struct {
	// Namespaces are the namespaces the child is cloned into (systemd
	// PrivateUsers=, PrivatePIDs=, PrivateMounts=, PrivateIPC=, ProtectHostname=,
	// PrivateNetwork=, ProtectControlGroups=). A mount namespace is unshared
	// with every mount of it made private first, so nothing the child mounts
	// propagates back. Without NamespaceUser, an unprivileged supervisor's
	// clone is refused by the kernel.
	Namespaces Namespace
	// UIDMap maps the user namespace's uids to the supervisor's (NamespaceUser).
	// Empty maps the namespace's uid 0 to the supervisor's effective uid, one id
	// wide — the one mapping an unprivileged supervisor may write.
	UIDMap []IDMapValue
	// GIDMap maps the user namespace's gids, as UIDMap maps the uids; setgroups(2)
	// is denied in the namespace.
	GIDMap []IDMapValue
	// Mounts are made in order in the child's mount namespace, which they
	// require (NamespaceMount).
	Mounts []MountValue
	// Root, when not empty, is the absolute directory the child pivots into
	// after its Mounts (systemd RootDirectory=, through pivot_root(2)); the old
	// root is detached. Spec.Path and Spec.Dir are then paths inside it, and
	// Spec.Path must hold a '/'. It requires NamespaceMount.
	Root string
	// Capabilities, when non-nil, bounds the child's capabilities; nil leaves
	// them as inherited.
	Capabilities *CapabilitiesValue
	// NoNewPrivileges sets no_new_privs (systemd NoNewPrivileges=): no execve of
	// the child or its descendants gains a privilege. Landlock and Seccomp set
	// it whatever this says, as the kernel requires of an unprivileged caller.
	NoNewPrivileges bool
	// Landlock, when non-nil, restricts the child's access to the filesystem.
	Landlock *LandlockValue
	// Seccomp, when non-nil, filters the child's system calls.
	Seccomp *SeccompValue
}

SandboxValue is the isolation a child is spawned under (Spec.Sandbox). The spawn applies it in two stages: the clone puts the child in its namespaces, then the child — re-executed from /proc/self/exe, on one locked thread — makes its mounts, pivots into its root, opens its Landlock paths, cuts its capability bounding set, sets its securebits, takes its identity, sets its capabilities, marks every descriptor past the ones it is given close-on-exec, sets no_new_privs, restricts itself with Landlock and installs its seccomp filter last, just before execve. A step that fails stops the spawn before the program runs (SandboxFailed); a part the kernel cannot apply is refused before the fork (SandboxUnavailable), and a platform without the mechanism refuses the whole value (UnsupportedPlatform). Linux on amd64 and arm64 applies it; every other platform refuses it.

type SchedPolicy added in v0.28.0

type SchedPolicy int

SchedPolicy is a CPU scheduling policy (Spec.Scheduling). Its values are Linux's SCHED_* numbers; the zero value SchedOther is the normal time-sharing policy.

func (SchedPolicy) String added in v0.28.0

func (p SchedPolicy) String() string

String returns the lowercase policy name ("other", "fifo", "rr", "batch", "idle"), or "schedpolicy(N)" for an undefined value.

type SchedulingValue added in v0.28.0

type SchedulingValue struct {
	// Policy is the CPU scheduling policy.
	Policy SchedPolicy
	// Priority is the static priority of SchedFIFO and SchedRR (1 to 99 on Linux,
	// 0 to 31 on FreeBSD); it must be 0 for every other policy.
	Priority int
	// IOClass is the I/O scheduling class; IOClassNone leaves the kernel's
	// default, derived from the CPU niceness.
	IOClass IOClass
	// IOPriority is the level within IOClassRealTime or IOClassBestEffort, from 0
	// (highest) to 7 (lowest).
	IOPriority int
}

SchedulingValue is the CPU scheduling policy and I/O priority a child is spawned under (Spec.Scheduling). Its zero value asks for the normal policy and no I/O class: SchedOther at priority 0, IOClassNone.

type SeccompAction added in v0.28.0

type SeccompAction uint8

SeccompAction is what a seccomp filter does with a system call. The zero value SeccompKill kills the process.

type SeccompValue added in v0.28.0

type SeccompValue struct {
	// Default is the action of every call Syscalls does not name.
	Default SeccompAction
	// Syscalls are the calls Action applies to, by their Linux names ("mkdirat").
	// A name no supported architecture knows is InvalidSpec; one this
	// architecture lacks matches nothing.
	Syscalls []string
	// Action is the action of the calls Syscalls names; it must differ from
	// Default.
	Action SeccompAction
	// Errno is the error number SeccompErrno returns, 1 to 4095; zero is EPERM.
	Errno int
}

SeccompValue is a child's system call filter (SandboxValue.Seccomp, systemd SystemCallFilter= and SystemCallErrorNumber=): the calls named in Syscalls take Action, every other call Default. A call from another architecture's ABI — or the x32 ABI on amd64 — kills the process. execve, exit, exit_group, rt_sigreturn and the spawn's own failure report always pass, since the filter is installed before the child's program is executed: a Syscalls entry naming one of the first four with an action other than SeccompAllow is InvalidSpec.

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. On Linux (SIGRTMIN 34 to SIGRTMAX 64, glibc's view) and FreeBSD (65 to 126) it also accepts the realtime signals as kill(1) and systemd spell them — "SIGRTMIN", "SIGRTMIN+3", "SIGRTMAX-2", "RTMIN+1" — and their numbers; elsewhere the same spellings are UnknownSignal.

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 or in its realtime range (Linux and FreeBSD).

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"). A realtime signal renders as "SIGRTMIN" or "SIGRTMIN+n" on Linux and FreeBSD. 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. A key
	// set more than once keeps its last value, at the place of its last
	// occurrence, as os/exec reads append(os.Environ(), "K=v"). A key beginning
	// __KITSUNIUM_SDK_PROC_, in any case, is the SDK's own spawn protocol and is
	// refused with ReservedEnv before any fork.
	Env []string

	// User is the name or numeric uid to setuid to; empty leaves the parent's.
	// The child takes the user's primary gid unless Group names one. A numeric
	// uid with no passwd entry has no primary gid, so it needs an explicit
	// Group: alone it fails with UnknownUser, never falling back to gid 0.
	User string
	// Group is the name or numeric gid to setgid to. Empty leaves the user's
	// primary gid when User is set, and the parent's when it is not.
	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
	// DieWithParent, when non-zero, is the signal the child receives when the
	// supervisor dies — a SIGKILL or a crash included, where no Stop runs. Linux and
	// FreeBSD set the kernel's parent-death signal (PR_SET_PDEATHSIG, procctl
	// PROC_PDEATHSIG_CTL), which fires when the THREAD that forked the child exits:
	// the Go runtime keeps its threads, but one whose goroutine ends while locked to
	// it exits with it. Windows places the child in a Job Object with
	// KILL_ON_JOB_CLOSE whose handle stays open for the supervisor's life, so any
	// non-zero signal means termination there. Elsewhere a non-zero value surfaces
	// UnsupportedPlatform before anything is spawned. The zero Signal requests
	// nothing.
	DieWithParent Signal

	// 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=). On Windows it selects the child's priority class at
	// creation: -10 and below HIGH, below 0 ABOVE_NORMAL, 0 NORMAL, 1 to 10
	// BELOW_NORMAL, above 10 IDLE.
	Nice *int
	// OOMScoreAdj, when non-nil, is the /proc/<pid>/oom_score_adj value applied
	// to the child (systemd OOMScoreAdjust=).
	OOMScoreAdj *int
	// Scheduling, when non-nil, is the CPU scheduling policy and the I/O priority
	// applied to the child (systemd CPUSchedulingPolicy=, CPUSchedulingPriority=,
	// IOSchedulingClass=, IOSchedulingPriority=), on the live pid beside Nice. Linux
	// applies both (sched_setscheduler, ioprio_set); FreeBSD applies SchedFIFO,
	// SchedRR and SchedIdle through rtprio(2); every other platform refuses a Policy
	// other than SchedOther or an IOClass other than IOClassNone with
	// UnsupportedPlatform before spawning. An undefined policy or class, or an I/O
	// level past 7, is InvalidSpec; a refusal by the kernel (a realtime policy
	// without CAP_SYS_NICE) is RlimitFailed and the child is torn down.
	Scheduling *SchedulingValue
	// 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. Without a ResourceNoFile entry the child
	// does not inherit the supervisor's open-file soft limit: the Go runtime
	// raises its own to the hard limit at start and puts the original back in
	// every child it forks, until rlimit.KeepRaisedNoFile is called.
	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). On Linux 5.7 and later the child is cloned straight
	// into the group (clone3 with CLONE_INTO_CGROUP); on an older kernel, or one
	// whose filter refuses clone3, the pre-exec trampoline writes its pid to
	// <CgroupPath>/cgroup.procs. 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),
	// StdioCapture (use the Stdout/Stderr/Stdin members below), or StdioPTY (one
	// pseudo-terminal for all three, sized by PTY). 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
	// PTY, when Stdio is StdioPTY, is the initial window size of the child's
	// pseudo-terminal; nil leaves the size a new terminal has (zero by zero). It is
	// ignored in every other mode.
	PTY *PTYValue
	// DrainGrace bounds how long Wait waits for the StdioCapture copiers once the
	// exit status is in hand. A process the child left behind that inherited its
	// stdout or stderr — a daemon, `sleep 60 &` — keeps the pipe open after the
	// child exits; when the grace closes, Wait closes the parent's pipe ends and
	// returns the real ExitValue with StdioDrainTimeout. The zero value is the
	// bounded default of one second (ADR 0031); a negative value waits for every
	// byte however long the pipes stay open.
	DrainGrace time.Duration

	// 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. They
	// are the only descriptors past stdio the child gets, unless FDs says otherwise.
	ExtraFiles []*os.File
	// PIDEnv names variables the child is given set to its own PID, in decimal:
	// the spawn sets them after the fork, before the program runs — what
	// LISTEN_PID and WATCHDOG_PID need, since no parent knows a child's PID
	// before forking it. A name here replaces the same name in Env. Setting one
	// routes the spawn through the trampoline, as Rlimits and Umask do; Windows
	// refuses it with UnsupportedPlatform. A name that is empty or holds '=',
	// ',' or NUL is InvalidSpec, one beginning __KITSUNIUM_SDK_PROC_ ReservedEnv.
	PIDEnv []string
	// FDs says which of the supervisor's descriptors the child inherits besides its
	// stdio and ExtraFiles. FDsSealed — the zero value — gives it none: Start first
	// marks every descriptor of the supervisor from 3 up close-on-exec, one
	// close_range(CLOSE_RANGE_CLOEXEC) on Linux 5.11 and later and on FreeBSD,
	// otherwise a walk of /proc/self/fd (Linux, illumos, Solaris) or /dev/fd (darwin
	// and the BSDs). The mark is process-wide and closes nothing: the supervisor keeps
	// using its descriptors. FDsInherited lets the child inherit every descriptor that
	// lacks close-on-exec, as fork/exec does by default — after a sealed spawn, only
	// those opened without close-on-exec since. Windows passes a child only the
	// handles it is given, so the field changes nothing there.
	FDs FDPolicy
	// Sandbox, when non-nil, is the isolation the child is spawned under:
	// namespaces, mounts, a new root, capabilities, no_new_privs, Landlock and
	// seccomp, applied by the spawn's second stage before the program runs, the
	// seccomp filter last. The identity (User, Group, Groups) is then taken by
	// that stage too, after the capability bounding set is cut, and FDs must be
	// FDsSealed. Linux on amd64 and arm64 applies it; any other platform refuses
	// it with UnsupportedPlatform before anything is spawned, a kernel lacking a
	// part of it with SandboxUnavailable, and a step that fails in the child with
	// SandboxFailed — the program never runs unconfined.
	Sandbox *SandboxValue
}

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
	// StdioPTY connects the child's stdin, stdout and stderr to the slave side of a
	// new pseudo-terminal, which becomes its controlling terminal in a new session;
	// the master is the handle's Terminal.TTY. Linux, darwin and FreeBSD; every
	// other platform refuses it with UnsupportedPlatform before spawning.
	StdioPTY
)

func (StdioMode) Known

func (m StdioMode) Known() bool

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

func (StdioMode) String

func (m StdioMode) String() string

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

type Terminal added in v0.28.0

type Terminal interface {
	// TTY returns the master side of the child's pseudo-terminal: what the child
	// writes is read from it, and what is written to it is the child's input. The
	// caller owns it and closes it; it is nil for a child started without
	// StdioPTY.
	TTY() *os.File
	// Resize sets the terminal's window size (TIOCSWINSZ), and the kernel sends
	// the child SIGWINCH. It returns PTYFailed for a child started without
	// StdioPTY or a size the kernel refuses.
	Resize(rows, cols uint16) error
	// Size reads the terminal's current window size (TIOCGWINSZ).
	Size() (PTYValue, error)
}

Terminal is the sibling of Process (ADR 0039) for a child started with StdioPTY: the master side of its pseudo-terminal, and the window size. The handle Start returns implements it on Unix.

type UsageValue added in v0.28.0

type UsageValue struct {
	// MinorFaults counts the page faults served without I/O (ru_minflt).
	MinorFaults uint64
	// MajorFaults counts the page faults that needed I/O (ru_majflt).
	MajorFaults uint64
	// InBlock counts the block input operations (ru_inblock).
	InBlock uint64
	// OutBlock counts the block output operations (ru_oublock).
	OutBlock uint64
	// VoluntaryCtxSw counts the times the process gave up the CPU (ru_nvcsw).
	VoluntaryCtxSw uint64
	// InvoluntaryCtxSw counts the times the scheduler took the CPU away (ru_nivcsw).
	InvoluntaryCtxSw uint64
}

UsageValue is the resource accounting wait4 returns beside CPU time and peak RSS: the page faults, block I/O operations and context switches of the finished process. It is the value ExitValue.Usage carries.

Directories

Path Synopsis
Package bpf is the contract of the BPF programs a supervisor attaches to a control group (ADR 0215): the policies as plain values — a device policy for cgroup_device, an IP policy with its counters for cgroup_skb, a bind policy for cgroup_sock_addr —, their validation, the ports a vendor module implements and an attachment satisfies, and the codes of the range 0.3.107.*.
Package bpf is the contract of the BPF programs a supervisor attaches to a control group (ADR 0215): the policies as plain values — a device policy for cgroup_device, an IP policy with its counters for cgroup_skb, a bind policy for cgroup_sock_addr —, their validation, the ports a vendor module implements and an attachment satisfies, and the codes of the range 0.3.107.*.
Package dbus is the contract of the SDK's message bus client (kitsunium/sdk#737, ADR 0214's S0e amendment): the codes its refusals carry, range 0.3.108.*, declared here at the engine's path (ADR 0160).
Package dbus is the contract of the SDK's message bus client (kitsunium/sdk#737, ADR 0214's S0e amendment): the codes its refusals carry, range 0.3.108.*, declared here at the engine's path (ADR 0160).
ipc
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).
rpc
Package rpc is the contract of a call between two processes over the private socket (ADR 0218): the Caller a client is, the Handler that answers one method, the Router that names it, the Request a handler reads, the Stream a caller reads a server's items from, and the codes every refusal carries — range 0.3.106.*, declared here at the engine's path (ADR 0160).
Package rpc is the contract of a call between two processes over the private socket (ADR 0218): the Caller a client is, the Handler that answers one method, the Router that names it, the Request a handler reads, the Stream a caller reads a server's items from, and the codes every refusal carries — range 0.3.106.*, declared here at the engine's path (ADR 0160).
Package service is the contract of a service unit's life cycle (ADR 0214): systemd.service(5)'s directives as typed values, the states a unit passes through and the snapshot a consumer reads, the ports the engine reaches the world through, and the codes of the range 0.3.105.*.
Package service is the contract of a service unit's life cycle (ADR 0214): systemd.service(5)'s directives as typed values, the states a unit passes through and the snapshot a consumer reads, the ports the engine reaches the world through, and the codes of the range 0.3.105.*.

Jump to

Keyboard shortcuts

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