Documentation
¶
Overview ¶
Package exec — Unix best-effort post-start attributes: scheduling priority (Nice), OOM-killer bias (OOMScoreAdj). Each is applied after fork/exec and surfaces a typed error when the host refuses, rather than silently dropping a requested field.
Package exec — Linux pre-exec cgroup v2 placement. When a Spec sets CgroupPath, the child must join that control group BEFORE it execs the target, so the controller limits (memory.max, pids.max, …) bind from the first instruction rather than after a post-spawn Group.Add(pid) race. The parent validates the path up front (validateCgroupPath); the trampoline then writes its own pid into <path>/cgroup.procs (applyCgroupPlacement) between the rlimit step and execve. cgroups are Linux-only, so this file is the only place that touches them; the !linux sibling rejects a non-empty path as UnsupportedPlatform.
Package exec — cgroup placement on Unix platforms that are NOT Linux (the BSDs, Darwin). cgroup v2 is a Linux mechanism with no portable equivalent, so a non-empty Spec.CgroupPath is rejected up front with UnsupportedPlatform rather than silently ignored — silently dropping the placement would run the target unconfined, the exact failure the feature exists to prevent. The trampoline never reaches applyCgroupPlacement here because validateCgroupPath fails the spawn first; it is defined only to keep the unix build self-contained.
Package exec — Windows cgroup-placement guard. A Spec.CgroupPath names a cgroup v2 directory, a Linux-only concept; Windows confines via Job Object assignment through the cgroup port (a handle, not a path), so a non-empty path at spawn has no meaning here and is rejected with the uniform sentinel.
Package exec — Unix credential resolution: maps Spec.User/Group/Groups (names or numeric ids) to a syscall.Credential via os/user, the systemd User=/Group=/SupplementaryGroups= analogue.
Package exec — the keystone spawn primitive: turns a coreproc.Spec into a running, supervised process via fork/exec with credentials, a private process group/session, and best-effort scheduling attributes.
The platform-portable surface is Start; the concrete fork/exec lives in exec_unix.go (every Unix GOOS) with a degrading no-op in exec_other.go so the package compiles on every target. Errors are the central coreproc sentinels, wrapped (never re-Defined) with errs.Wrap.
Package exec — the Unix spawn: validates the Spec, resolves credentials, builds the SysProcAttr (Setpgid/Setsid/Credential), forks/execs via os.StartProcess, then applies best-effort scheduling attributes. Returns a *Handle satisfying the coreproc.Process port.
Package exec — the Windows spawn primitive. Start turns a Spec into a running, supervised process via os.StartProcess (CreateProcess under the hood) and returns a coreproc.Process handle. Stdio (inherit/null/capture) reuses the portable stdioState; Setpgid maps to a new console process group so a later CTRL_BREAK can target the child. The Unix-only Spec fields — rlimits, umask, nice/oom, credentials, ExtraFiles, a cgroup path — have no Windows equivalent in this layer (resource confinement is the Job Object backend's job, applied at the cgroup port), so they are rejected up front with the uniform sentinel rather than silently dropped.
Package exec — the process-handle value: a Unix supervision handle implementing the coreproc.Process port over an *os.Process, with group-aware Signal/Stop and a once-only Wait that captures exit status plus wait4 rusage. The concrete type is unexported; Start returns it as the coreproc.Process interface.
Package exec — the Windows process-handle value implementing the coreproc.Process port over an *os.Process. Windows has no Unix process groups or wait4 rusage, so SignalGroup degrades to the leader and the ExitValue carries the exit code + CPU times the os layer exposes (no terminating signal, no MaxRSS). The concrete type is unexported; Start returns it as the interface.
Package exec — the parent<->trampoline handshake pipe. The re-exec trampoline (trampoline_unix.go) applies rlimits/umask in a forked child before exec'ing the real target; if that application fails, or the execve itself fails, the child exits with a distinct status — but Start could not otherwise tell that apart from a legitimate target exit of the same code. This pipe closes the gap: the child inherits the write end as handshakeFD and reports a one-byte status through it, so Start surfaces a typed RlimitFailed / SpawnFailed instead of a silent spawn. It is the same self-pipe + close-on-exec trick os/exec uses for its own errpipe: a clean execve closes the fd (the parent reads EOF = success), a failure writes a status byte first.
Package exec — spawn-time resource confinement via a Win32 Job Object: the Windows realisation of a Spec's rlimits. A child requesting a mappable Resource is assigned, right after spawn, to a Job Object carrying the limit, so the rlimit "grafts onto" the spawn (the cgroup port is the standalone equivalent). kernel32 is bound directly (syscall.NewLazyDLL, ABI cited, no x/sys).
Mappable: ResourceAS/ResourceData → ProcessMemoryLimit; ResourceNProc → ActiveProcessLimit. Every other Resource (NoFile/Core/FSize/Stack/MemLock) has no Job Object analogue and is rejected with UnknownResource before the spawn, honouring the no-silent-field-loss rule.
Package exec — Unix umask / rlimit validation for the spawn. The Go runtime exposes no SysProcAttr hook to run setrlimit(2)/umask(2) in the child between fork and exec, so Start honours both via the re-exec trampoline (trampoline_unix.go): a Spec requesting a mapped Rlimit or a Umask is spawned through a self-invocation that applies the limits then execs the target. This validator therefore only rejects a Resource with no platform RLIMIT_* mapping (the SDK's "no silent field loss" rule) — every mappable field is honoured.
Package exec — RLIMIT_AS mapping for the platforms whose stdlib syscall exports it (Linux, Darwin, FreeBSD, NetBSD, DragonFly). OpenBSD has no address-space rlimit — RLIMIT_AS is absent from its kernel ABI — so it provides the no-op in limittable_openbsd.go instead, leaving ResourceAS unmapped (UnknownResource).
Package exec — OpenBSD has no address-space rlimit (RLIMIT_AS is absent from its kernel ABI), so it adds no resources beyond the common Unix set. ResourceAS therefore stays unmapped on OpenBSD and a Spec requesting it surfaces the typed UnknownResource — the honest "this platform cannot set that limit" answer, rather than a build failure on the missing constant.
Package exec — Unix Resource→RLIMIT_* table. Only the constants the stdlib syscall package exports are mapped; RLIMIT_NPROC and RLIMIT_MEMLOCK live in golang.org/x/sys (banned here), so those resources stay unmapped and surface UnknownResource rather than a wrong limit. The address-space limit (RLIMIT_AS) is present on every supported target EXCEPT OpenBSD, so it is added by a build-tagged addPlatformLimits (limittable_as.go / limittable_openbsd.go) rather than referenced here — that keeps this file compiling on OpenBSD.
Package exec — resolving a bare executable name through the PATH the child will run with.
os.StartProcess does not search PATH: it hands its path to execve (or to CreateProcess) as written, so a bare "go" names a file in the current directory and fails with ENOENT. The port documents Spec.Path as "absolute or PATH-resolvable", which is what os/exec.Command gives a Go programmer, so the resolution happens here, once, before either spawn path — the direct fork/exec and the limits trampoline, which execs the target itself.
Package exec — the Unix spellings of a PATH search: one candidate per directory, executable when any execute bit is set, variable names case-sensitive.
Package exec — the Windows spellings of a PATH search: a name is tried with each PATHEXT extension unless it already carries one, a regular file is runnable by its extension, and variable names are case-insensitive ("Path" is the usual spelling there).
Package exec — the peak-RSS unit: peakKiB turns a raw ru_maxrss into the KiB the port documents, passing a kilobyte figure through and converting darwin's bytes rounding up. It is platform-neutral so its arithmetic is tested on every GOOS.
Package exec — peak-RSS read on a 32-bit GOARCH, where Rusage.Maxrss is int32 and is widened to the int64 the port expects (see maxrss_rss64_unix.go for the 64-bit counterpart).
Package exec — peak-RSS read on a 64-bit GOARCH, where Rusage.Maxrss is already int64 and needs no widening cast (see maxrss_rss32_unix.go for the 32-bit counterpart).
Package exec — darwin's ru_maxrss unit: its getrusage(2) and wait4(2) report bytes, so 1024 of them make one KiB. The darwin constraint also holds for GOOS=ios.
Package exec — the ru_maxrss unit of every other Unix: Linux and the BSDs report kilobytes, so the figure passes through; illumos and Solaris define it in pages and leave it 0.
Package exec — Unix procfs writer: a tiny os.WriteFile shim used to set /proc/<pid>/oom_score_adj, isolated so the platform-specific path handling stays out of the attribute logic.
Package exec — per-process stdio wiring. buildStdio turns a Spec's Stdio mode into the three *os.File the spawn passes as ProcAttr.Files, plus the parent-side lifecycle: the child-side ends to close once the child owns its dups (so EOF propagates on exit) and the copier goroutines that drain capture pipes into the caller's writers. StdioInherit (default) shares the parent's streams; StdioNull discards via the null device; StdioCapture connects pipes.
Package exec — the re-exec trampoline that honours Spec.Rlimits and Spec.Umask pre-exec. The Go runtime exposes no SysProcAttr hook to run setrlimit(2)/ umask(2) in the child between fork and exec, so when a Spec requests either, Start does NOT exec the target directly: it execs this very binary (os.Executable()) as a tiny trampoline, passing the real target + an encoded limits payload in a sentinel env var. The init() below detects that var, applies the limits in the fresh process (before main), then execve()s the real target — so the target starts already under its rlimits/umask, with no cgo and no dependency. The limits persist across the execve.
Footgun: any binary that imports this package and is run with the sentinel env var set will re-exec. Start sets it only on the trampoline child and strips it before execve, so a normal run never has it; do not set it by hand.
Package exec — the sandbox (Spec.Sandbox, #728), a trampoline in two stages on Linux amd64 and arm64. Stage one is the clone: the namespaces, the mount namespace unshared and made private, the user namespace's maps, and the exec of /proc/self/exe — never a path that may since hold another binary; without procfs the spawn is refused. Stage two runs in the child before main, on its one locked thread, from a payload sealed in a memfd (a version, a length, every seal): mounts, pivot_root, the Landlock paths, PR_CAPBSET_DROP, securebits, setgroups/setresgid/setresuid through raw calls on that thread, capset and the ambient set, the close-on-exec sweep, no_new_privs, landlock_restrict_self, and the seccomp filter — an architecture check, x32 killed on amd64 — immediately before execve. A step that fails reports itself on the handshake pipe and the target never runs. The system call numbers and the seccomp name table are per GOARCH (sysnum_linux_amd64.go, sysnum_linux_arm64.go); every other platform refuses the field before the fork.
Spikes, not implemented: OpenBSD's pledge(2) execpromises and unveil(2), and Windows' restricted tokens and AppContainer. Verdict: neither maps onto the Linux steps — pledge restricts by promise, not by system call or path tree, and unveil does not survive execve; a restricted token or an AppContainer is set at CreateProcessAsUser, with no namespace, mount, Landlock or seccomp equivalent. Both stay refused with UnsupportedPlatform until a value of their own is designed for them.
Package exec — central wrap helpers. Every syscall/stdlib cause is wrapped back onto a coreproc sentinel by restating that sentinel's exact Reason/Public/Private/ExitCode here once, so call sites stay terse and the magic exit-code literals live in a single named place.
Package exec — darwin's word on whether this process's unreaped child has died: the probe zombieGroupRefused needs because darwin's kill(2) answers a group of zombies with EPERM.
Package exec — the zombie probe where the kernel needs none.
Index ¶
- func Attach(ctx context.Context, pid int) (proc coreproc.Process, err error)
- func AttachPIDFile(ctx context.Context, path string, want coreproc.AttachValue) (proc coreproc.Process, err error)
- func EnvAllow(parent []string, keys ...string) []string
- func EnvDeny(parent []string, keys ...string) []string
- func LandlockABI() (abi int, err error)
- func Open(pid int) (proc coreproc.Process, err error)
- func Reclaim(ctx context.Context, pid int) (proc coreproc.Process, err error)
- func Run(ctx context.Context, spec coreproc.Spec, opts coreproc.RunValue) (result coreproc.ResultValue, err error)
- func Start(ctx context.Context, spec coreproc.Spec) (proc coreproc.Process, err error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Attach ¶ added in v0.28.0
Attach adopts a running process this one did not spawn and holds it so that its Wait observes the end and nothing it sends reaches another process that reuses the pid: through a pidfd on Linux, through two kqueue EVFILT_PROC knotes on darwin and the BSDs, each attached by the kernel to the process that had the pid when it was registered and to no later one. Wait and WaitContext return once the process has exited, with StatusUnknown and an ExitValue whose Code is -1: only a parent reads an exit status, and none is promised. Nothing is waited for in the kernel's sense, so a child of this process attached here keeps its status for its own handle or the reaper. On Linux Signal, SignalGroup and SignalHandle go through the pidfd. On darwin and the BSDs no kernel handle signals a process this one did not fork: Signal and SignalGroup go by pid once a knote has proven the process still running — the instant between that proof and kill(2) is the one a reuse could still slip into —, SignalHandle is UnsupportedPlatform and Handle reports none. Stop signals, watches for the end within the grace, then kills. The handle implements ProcessHandle and ContextWaiter.
A pid below 1 is InvalidSpec; PID 1 and this process are AdoptionRefused; a pid that does not exist is SignalFailed wrapping ESRCH; a kernel without pidfd_open, or a kqueue refused, is HandleUnavailable; illumos, Solaris and Windows are UnsupportedPlatform. ctx is read before any kernel call.
func AttachPIDFile ¶ added in v0.28.0
func AttachPIDFile(ctx context.Context, path string, want coreproc.AttachValue) (proc coreproc.Process, err error)
AttachPIDFile reads the PID file at path and adopts the process it names, as Attach does. path is absolute and walked one component at a time from the root; the file at its end is opened without following a symbolic link, without blocking, and must be a regular file holding one decimal PID, surrounding blanks allowed. Anything else is PIDFileRefused.
The file is trustworthy when nobody but root and this process's user could have made what the walk went through: every directory and the file belong to one of them, no directory is writable by its group or others unless sticky, the file by neither, and a symbolic link on the way lies in such a directory and belongs to one of them. The process a trustworthy file names is adopted; from any other file it must satisfy want, and a want that bounds nothing is AdoptionRefused. A bound want gives is checked whatever the file, and the check is proven to concern the process held, not a later one with its pid.
Off Linux there is no cgroup, so a Cgroup bound is AdoptionRefused; the users are read from the kernel's process record (sysctl kinfo_proc), whose start time must be the same before and after the knotes are registered. Each directory of the walk is held open for reading there, where Linux needs only the permission to search it.
func EnvAllow ¶ added in v0.28.0
EnvAllow returns the entries of parent whose key is one of keys, in parent's order: the environment to hand a child that must see those variables and no other. A key that appears more than once keeps its last value, at the place of its last occurrence — the value a getenv would read. Keys compare case-insensitively on Windows and exactly elsewhere. parent is not modified.
func EnvDeny ¶ added in v0.28.0
EnvDeny returns the entries of parent whose key is none of keys, in parent's order, a repeated key keeping its last value at the place of its last occurrence. Keys compare as in EnvAllow. parent is not modified.
func LandlockABI ¶ added in v0.28.0
LandlockABI returns the Landlock ABI version the running kernel implements: 1 on Linux 5.13, 2 on 5.19, 3 on 6.2, 4 on 6.7, 5 on 6.10, 6 on 6.12. A LandlockValue's rights newer than it are refused with SandboxUnavailable unless named best-effort. A kernel without Landlock, or with Landlock disabled at boot, is SandboxUnavailable; every platform but Linux on amd64 and arm64 is UnsupportedPlatform.
func Open ¶ added in v0.28.0
Open returns a handle on a process this one did not spawn, held through a kernel handle so that no later signal reaches another process that reuses the pid: a pidfd on Linux 5.4 and later, a process HANDLE on Windows. Its Signal and SignalHandle act through that handle; SignalGroup reaches the process alone, since a group addressed by number is not race-free; Stop signals, watches the handle for the exit within the grace, then kills. It implements ProcessHandle and ContextWaiter, and its Wait and WaitContext are refused with WaitFailed: only a parent collects an exit status. A pid that does not exist is SignalFailed wrapping ESRCH; a Linux kernel without pidfd is HandleUnavailable; every other platform is UnsupportedPlatform.
func Reclaim ¶ added in v0.28.0
Reclaim holds again a child this process spawned before an execve kept it — a supervisor's hand-over (kitsunium/sdk#738) —: a pidfd and the claim on its exit status (ADR 0093), so Wait reads its real status and reaps it, as Start's handle does, and the reaper hands it the status a sweep took. The child must be this process's, read from /proc/<pid>/stat before and after the claim: a pid gone, reaped already or another's is AdoptionRefused, a pid below 1 InvalidSpec. Its process group is its own when it leads one. Linux only; UnsupportedPlatform elsewhere. ctx is read before any kernel call.
func Run ¶ added in v0.28.0
func Run(ctx context.Context, spec coreproc.Spec, opts coreproc.RunValue) (result coreproc.ResultValue, err error)
Run starts spec, waits for it, and returns its exit with the head of its output, bounded however much the child writes: stdout and stderr are captured into buffers that keep the first opts.MaxOutputBytes bytes each and discard the rest while still reading, so the child never blocks on a full pipe and the supervisor never holds more than the ceiling. spec's Stdio, Stdout and Stderr are replaced; its Stdin is kept. When ctx ends first the child is stopped — SIGTERM, opts.Grace, then SIGKILL — and Run returns what it had with ctx's error. A MaxOutputBytes that is not positive is refused with InvalidSpec before anything is spawned.
func Start ¶
Start spawns the process described by spec and returns a live coreproc.Process handle. ctx is honoured up to the fork/exec boundary: a ctx already cancelled is reported before any OS work. The child runs with an explicit (possibly empty) environment, the requested credentials, and — when Setpgid is set — its own process group so the whole tree can be group-killed.
Types ¶
This section is empty.
Source Files
¶
- attach.go
- attach_handle.go
- attach_linux.go
- attrs_unix.go
- cgroup_clone_linux.go
- cgroup_placement_linux.go
- closerange_linux.go
- creds_unix.go
- diewithparent_pdeathsig.go
- doc.go
- env.go
- exec.go
- exec_unix.go
- fddir_proc.go
- foreign_wait.go
- handle_pidfd_linux.go
- handle_unix.go
- handshake_unix.go
- limits_unix.go
- limittable_as.go
- limittable_unix.go
- lookpath.go
- lookpath_unix.go
- oomadj_linux.go
- open.go
- open_linux.go
- pidfile.go
- pidfile_linux.go
- procfile_unix.go
- pty_linux.go
- pty_supported.go
- reclaim.go
- reclaim_linux.go
- run.go
- sandbox_linux.go
- sandbox_payload_linux.go
- sandbox_seccomp_linux.go
- sandbox_stage_linux.go
- sandbox_steps_unix.go
- scheduling.go
- scheduling_linux.go
- seal_closerange.go
- seal_unix.go
- stdio.go
- sysnum_linux_amd64.go
- trampoline_unix.go
- wrap.go
- zombie_other.go