process

package
v0.19.2 Latest Latest
Warning

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

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

README

process

import "github.com/kitsunium/sdk/pkg/v1/proc/process"

Package process is the public facade for the SDK's keystone process-spawn primitive: it starts a child process under explicit credentials, in its own process group and session, and returns a handle that can wait for exit, signal the leader or its whole group, and stop the group gracefully with a SIGTERM→SIGKILL escalation.

It is a thin facade over internal/service/proc/exec. The public types are aliases of the internal/core/proc port, so a value built here is the same type the service layer consumes — no conversion, no parallel hierarchy.

Spec → systemd directive mapping
Spec.Path / Args / Dir / Env   ExecStart= / WorkingDirectory= / Environment=
Spec.User / Group / Groups     User= / Group= / SupplementaryGroups=
Spec.Setpgid                   KillMode=control-group (group-killable tree)
Spec.Setsid                    a new session, detached from the parent tty
Spec.Nice                      Nice=
Spec.OOMScoreAdj               OOMScoreAdjust=
Spec.Umask                     UMask=        (applied via trampoline)
Spec.Rlimits                   Limit*=       (applied via trampoline)
Environment, and finding the executable

A nil Spec.Env yields an empty environment — the child never silently inherits the supervisor's, so a spawned service starts from a known state. Pass an explicit slice (including os.Environ()) to inherit deliberately.

Spec.Path may be a bare name. "go" is searched in the PATH the CHILD will see — the PATH entry of Spec.Env whenever it names one, even empty, the parent's only when it names none — with os/exec's rules: 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, which the returned SpawnFailed wraps (errors.Is answers). A path with a separator is taken as written.

Standard streams

Spec.Stdio selects how the child's stdin/stdout/stderr are wired: StdioInherit (the default) shares the parent's streams; StdioNull discards the child's output and gives it an immediate-EOF stdin; StdioCapture connects Spec.Stdout and Spec.Stderr (any io.Writer) and Spec.Stdin (any io.Reader), with a nil stream falling back to the null device for that one stream. In capture mode every byte the child writes reaches the writers before Wait returns, and the copier goroutines terminate at EOF (no leak). It composes with Setpgid/Setsid and credential drop. Stdin should be EOF-terminating.

Stopping a process group

Stop sends the chosen signal to the entire process group, waits up to grace for exit, then escalates to SIGKILL on the group. Because the child leads its own group (Spec.Setpgid), forked grandchildren are reached too — no survivor is left behind.

Example
spec := process.Spec{
	Path:    "/bin/sh",
	Args:    []string{"sh", "-c", "sleep 1000 & wait"},
	Setpgid: true,
}
p, err := process.Start(context.Background(), spec)
if err != nil {
	// err carries a central proc sentinel code (errs.HasCode).
	return err
}
// Graceful stop: SIGTERM to the group, escalate to SIGKILL after 5s.
if err := p.Stop(context.Background(), 5*time.Second, process.SIGTERM); err != nil {
	return err
}
exit, err := p.Wait()
_ = exit // exit.Code / exit.Signal / exit.UserTime / exit.MaxRSS
Resource limits and umask

The Go runtime exposes no hook to run setrlimit(2) or umask(2) in the child between fork and exec, so Start honours Spec.Rlimits and Spec.Umask via a re-exec trampoline: when either is set, Start spawns this binary as a tiny self-trampoline that applies the limits in the fresh child, then execs the real target — so the target starts already under its limits, with no cgo and no dependency. An Rlimit naming a resource with no RLIMIT_* mapping on the platform (NPROC, MEMLOCK — absent from stdlib syscall) is still rejected up front with UnknownResource; a limit the kernel refuses to set (e.g. raising a hard cap unprivileged) fails the spawn rather than running the child unconfined. Nice and OOMScoreAdj are applied post-start on the live pid and surface a typed error if the host refuses.

Platforms

Unix gets everything above. Windows spawns too, through CreateProcess: stdio wiring, Setpgid (a new console process group), Spec.Rlimits (enforced by a Job Object) and the PATH search (with PATHEXT) all work, while the fields with no Windows meaning at this layer — User/Group/Groups, Umask, Nice, OOMScoreAdj, ExtraFiles, CgroupPath — are refused with UnsupportedPlatform rather than dropped. There, SignalGroup reaches the leader only and Stop's escalation is TerminateProcess. Every other platform returns UnsupportedPlatform from Start; the package compiles everywhere.

The process itself

Everything above acts on a child. Self and Build read the process they run in, on every platform, and never fail:

stats := process.Self()
stats.Goroutines, stats.HeapBytes, stats.GCPauses.Quantile(0.99), stats.CPUTime

build, ok := process.Build()
sdk, found := build.Module("github.com/kitsunium/sdk")
// sdk.Version is a release ("v0.4.6"), or "" with sdk.Revision/sdk.Time
// for a pseudo-version, or "" with sdk.Local for a directory.

CPUTime is the kernel's count (getrusage) where the platform has one and the Go runtime's estimate elsewhere — refreshed only at a garbage collection; Stats.CPUEstimated says which. The distributions and every counter are cumulative since the process started — subtract two snapshots for a window.

A module's recorded version conflates three things, and Module keeps them apart: a RELEASE in Version, a COMMIT in Revision and Time (a pseudo-version names one, and so does the main module's version-control stamp), and a DIRECTORY in Local and Dir (a replace, a workspace module). What is in a local directory NOW is a question for the framework's git.Head (ADR 0158).

Package process — the running process itself: what it was built from and what it is doing.

Package process — ergonomic re-exports: the handful of signal constants and resource sentinels callers need to drive Stop/SignalGroup and read typed errors without importing internal/core/proc directly.

Package process — StdioMode re-exports for wiring a child's standard streams.

Index

Constants

The common control signals as typed process.Signal values, so callers pass process.SIGTERM to Stop/Signal without a syscall import. They equal any Signal parsed from the same name (the underlying value is the platform number).

const (
    // SIGTERM is the polite termination request — the default Stop signal.
    SIGTERM = Signal(syscall.SIGTERM)
    // SIGKILL is the unignorable kill Stop escalates to after the grace window.
    SIGKILL = Signal(syscall.SIGKILL)
    // SIGINT is the interrupt signal (Ctrl-C analogue).
    SIGINT = Signal(syscall.SIGINT)
    // SIGHUP is the hang-up / reload signal many daemons treat as "reconfigure".
    SIGHUP = Signal(syscall.SIGHUP)
    // SIGQUIT is the quit-with-core signal.
    SIGQUIT = Signal(syscall.SIGQUIT)
)

type BuildInfo

BuildInfo is what the running binary was built from: the toolchain, the main package, the main module with its version-control stamp, and every dependency followed through its replacement. It is an alias of the service value.

type BuildInfo = svcself.BuildValue

func Build
func Build() (info BuildInfo, ok bool)

Build describes what the running binary was built from, and reports false when the binary carries no build information.

func ParseBuild
func ParseBuild(info *debug.BuildInfo) BuildInfo

ParseBuild describes a BuildInfo from elsewhere — one a test builds by hand, or debug.ParseBuildInfo's reading of another binary — exactly as Build describes the running one.

type Distribution

Distribution is one of the Go runtime's cumulative histograms, read as durations: Count, and Quantile(q) as the upper bound of the bucket that reaches q. It is an alias of the service value.

type Distribution = svcself.DistributionValue

type ExitResult

ExitResult is the outcome of a finished process — exit code, terminating signal, and resource usage. It is an alias of the core port's ExitValue.

type ExitResult = coreproc.ExitValue

type Limit

Limit is a soft/hard resource-limit pair for setrlimit(2). It is an alias of the core port's LimitValue.

type Limit = coreproc.LimitValue

type Module

Module is one module of the running binary, with its release, its commit and its local directory told apart. It is an alias of the service value.

type Module = svcself.ModuleValue

type Process

Process is the handle to a spawned process: PID, Wait, Signal, SignalGroup, and a group-aware Stop. It is an alias of the core port interface.

type Process = coreproc.Process

func MustStart
func MustStart(ctx context.Context, spec Spec) Process

MustStart is like Start but panics with the typed error when the spawn fails — UnsupportedPlatform off Unix/Windows, InvalidSpec, RlimitFailed, … It is the idiomatic Go MustX opt-in (like regexp.MustCompile) for a consumer that chooses crash-on-failure at its own startup; the SDK itself never panics, and Start is the non-panicking form for normal use. The panic value is the typed error, so a top-level recover() can classify it via errs.CodeOf / HasCode.

func Start
func Start(ctx context.Context, spec Spec) (proc Process, err error)

Start spawns the process described by spec and returns a live Process handle. It delegates to internal/service/proc/exec; ctx is honoured up to the fork/exec boundary. A bare Spec.Path is searched in the child's PATH (see the package documentation). On Windows the Unix-only Spec fields are refused with UnsupportedPlatform; on platforms that are neither Unix nor Windows, Start itself returns UnsupportedPlatform.

type Resource

Resource identifies a per-process resource governed by setrlimit(2). It is an alias of the core port type.

type Resource = coreproc.Resource

The resource sentinels callers most often set in Spec.Rlimits, re-exported so a Spec can be built without importing internal/core/proc. The full set lives on the core Resource type.

const (
    // ResourceNoFile limits the highest open file descriptor (RLIMIT_NOFILE).
    ResourceNoFile Resource = coreproc.ResourceNoFile
    // ResourceCore limits the size of a core dump in bytes (RLIMIT_CORE).
    ResourceCore Resource = coreproc.ResourceCore
    // ResourceCPU limits CPU time in seconds (RLIMIT_CPU).
    ResourceCPU Resource = coreproc.ResourceCPU
    // ResourceAS limits the process virtual address-space size (RLIMIT_AS).
    ResourceAS Resource = coreproc.ResourceAS
)

type Signal

Signal is a typed, platform-portable OS signal. It is an alias of the core port type, so process.SIGTERM and a signal parsed elsewhere compare equal.

type Signal = coreproc.Signal

type Spec

Spec is the immutable description of a process to spawn — executable, environment, credentials, isolation topology, and scheduling attributes. It is an alias of the core port type.

type Spec = coreproc.Spec

type Stats

Stats is the running process at one instant: identity, scheduler settings, goroutines, heap, collections with their pause distribution, scheduling latency, and CPU time. Every count is cumulative since the process started. It is an alias of the service value.

type Stats = svcself.StatsValue

func Self
func Self() Stats

Self takes a snapshot of the running process. It never fails: a figure the platform cannot give is zero, and Stats.CPUEstimated says when CPUTime is the runtime's estimate rather than the kernel's count.

type StdioMode

StdioMode selects how a spawned child's standard streams (stdin, stdout, stderr) are wired. It is an alias of the core port type; set it on Spec.Stdio. The zero value is StdioInherit.

type StdioMode = coreproc.StdioMode

StdioCapture connects the child to Spec.Stdout/Stderr (io.Writer) and Spec.Stdin (io.Reader); a nil stream falls back to the null device for that one stream. The systemd analogue is StandardOutput=/StandardError=/StandardInput=.

const StdioCapture StdioMode = coreproc.StdioCapture

StdioInherit shares the parent's stdin/stdout/stderr with the child — the default and the only behaviour before per-process wiring existed.

const StdioInherit StdioMode = coreproc.StdioInherit

StdioNull connects every stream to the null device: output is discarded and the child's stdin returns EOF immediately.

const StdioNull StdioMode = coreproc.StdioNull

Generated by gomarkdoc

Documentation

Overview

Package process is the public facade for the SDK's keystone process-spawn primitive: it starts a child process under explicit credentials, in its own process group and session, and returns a handle that can wait for exit, signal the leader or its whole group, and stop the group gracefully with a SIGTERM→SIGKILL escalation.

It is a thin facade over internal/service/proc/exec. The public types are aliases of the internal/core/proc port, so a value built here is the same type the service layer consumes — no conversion, no parallel hierarchy.

Spec → systemd directive mapping

Spec.Path / Args / Dir / Env   ExecStart= / WorkingDirectory= / Environment=
Spec.User / Group / Groups     User= / Group= / SupplementaryGroups=
Spec.Setpgid                   KillMode=control-group (group-killable tree)
Spec.Setsid                    a new session, detached from the parent tty
Spec.Nice                      Nice=
Spec.OOMScoreAdj               OOMScoreAdjust=
Spec.Umask                     UMask=        (applied via trampoline)
Spec.Rlimits                   Limit*=       (applied via trampoline)

Environment, and finding the executable

A nil Spec.Env yields an empty environment — the child never silently inherits the supervisor's, so a spawned service starts from a known state. Pass an explicit slice (including os.Environ()) to inherit deliberately.

Spec.Path may be a bare name. "go" is searched in the PATH the CHILD will see — the PATH entry of Spec.Env whenever it names one, even empty, the parent's only when it names none — with os/exec's rules: 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, which the returned SpawnFailed wraps (errors.Is answers). A path with a separator is taken as written.

Standard streams

Spec.Stdio selects how the child's stdin/stdout/stderr are wired: StdioInherit (the default) shares the parent's streams; StdioNull discards the child's output and gives it an immediate-EOF stdin; StdioCapture connects Spec.Stdout and Spec.Stderr (any io.Writer) and Spec.Stdin (any io.Reader), with a nil stream falling back to the null device for that one stream. In capture mode every byte the child writes reaches the writers before Wait returns, and the copier goroutines terminate at EOF (no leak). It composes with Setpgid/Setsid and credential drop. Stdin should be EOF-terminating.

Stopping a process group

Stop sends the chosen signal to the entire process group, waits up to grace for exit, then escalates to SIGKILL on the group. Because the child leads its own group (Spec.Setpgid), forked grandchildren are reached too — no survivor is left behind.

Example

spec := process.Spec{
	Path:    "/bin/sh",
	Args:    []string{"sh", "-c", "sleep 1000 & wait"},
	Setpgid: true,
}
p, err := process.Start(context.Background(), spec)
if err != nil {
	// err carries a central proc sentinel code (errs.HasCode).
	return err
}
// Graceful stop: SIGTERM to the group, escalate to SIGKILL after 5s.
if err := p.Stop(context.Background(), 5*time.Second, process.SIGTERM); err != nil {
	return err
}
exit, err := p.Wait()
_ = exit // exit.Code / exit.Signal / exit.UserTime / exit.MaxRSS

Resource limits and umask

The Go runtime exposes no hook to run setrlimit(2) or umask(2) in the child between fork and exec, so Start honours Spec.Rlimits and Spec.Umask via a re-exec trampoline: when either is set, Start spawns this binary as a tiny self-trampoline that applies the limits in the fresh child, then execs the real target — so the target starts already under its limits, with no cgo and no dependency. An Rlimit naming a resource with no RLIMIT_* mapping on the platform (NPROC, MEMLOCK — absent from stdlib syscall) is still rejected up front with UnknownResource; a limit the kernel refuses to set (e.g. raising a hard cap unprivileged) fails the spawn rather than running the child unconfined. Nice and OOMScoreAdj are applied post-start on the live pid and surface a typed error if the host refuses.

Platforms

Unix gets everything above. Windows spawns too, through CreateProcess: stdio wiring, Setpgid (a new console process group), Spec.Rlimits (enforced by a Job Object) and the PATH search (with PATHEXT) all work, while the fields with no Windows meaning at this layer — User/Group/Groups, Umask, Nice, OOMScoreAdj, ExtraFiles, CgroupPath — are refused with UnsupportedPlatform rather than dropped. There, SignalGroup reaches the leader only and Stop's escalation is TerminateProcess. Every other platform returns UnsupportedPlatform from Start; the package compiles everywhere.

The process itself

Everything above acts on a child. Self and Build read the process they run in, on every platform, and never fail:

stats := process.Self()
stats.Goroutines, stats.HeapBytes, stats.GCPauses.Quantile(0.99), stats.CPUTime

build, ok := process.Build()
sdk, found := build.Module("github.com/kitsunium/sdk")
// sdk.Version is a release ("v0.4.6"), or "" with sdk.Revision/sdk.Time
// for a pseudo-version, or "" with sdk.Local for a directory.

CPUTime is the kernel's count (getrusage) where the platform has one and the Go runtime's estimate elsewhere — refreshed only at a garbage collection; Stats.CPUEstimated says which. The distributions and every counter are cumulative since the process started — subtract two snapshots for a window.

A module's recorded version conflates three things, and Module keeps them apart: a RELEASE in Version, a COMMIT in Revision and Time (a pseudo-version names one, and so does the main module's version-control stamp), and a DIRECTORY in Local and Dir (a replace, a workspace module). What is in a local directory NOW is a question for the framework's git.Head (ADR 0158).

Package process — the running process itself: what it was built from and what it is doing.

Package process — ergonomic re-exports: the handful of signal constants and resource sentinels callers need to drive Stop/SignalGroup and read typed errors without importing internal/core/proc directly.

Package process — StdioMode re-exports for wiring a child's standard streams.

Index

Constants

View Source
const (
	// SIGTERM is the polite termination request — the default Stop signal.
	SIGTERM = Signal(syscall.SIGTERM)
	// SIGKILL is the unignorable kill Stop escalates to after the grace window.
	SIGKILL = Signal(syscall.SIGKILL)
	// SIGINT is the interrupt signal (Ctrl-C analogue).
	SIGINT = Signal(syscall.SIGINT)
	// SIGHUP is the hang-up / reload signal many daemons treat as "reconfigure".
	SIGHUP = Signal(syscall.SIGHUP)
	// SIGQUIT is the quit-with-core signal.
	SIGQUIT = Signal(syscall.SIGQUIT)
)

The common control signals as typed process.Signal values, so callers pass process.SIGTERM to Stop/Signal without a syscall import. They equal any Signal parsed from the same name (the underlying value is the platform number).

Variables

This section is empty.

Functions

This section is empty.

Types

type BuildInfo

type BuildInfo = svcself.BuildValue

BuildInfo is what the running binary was built from: the toolchain, the main package, the main module with its version-control stamp, and every dependency followed through its replacement. It is an alias of the service value.

func Build

func Build() (info BuildInfo, ok bool)

Build describes what the running binary was built from, and reports false when the binary carries no build information.

func ParseBuild

func ParseBuild(info *debug.BuildInfo) BuildInfo

ParseBuild describes a BuildInfo from elsewhere — one a test builds by hand, or debug.ParseBuildInfo's reading of another binary — exactly as Build describes the running one.

type Distribution

type Distribution = svcself.DistributionValue

Distribution is one of the Go runtime's cumulative histograms, read as durations: Count, and Quantile(q) as the upper bound of the bucket that reaches q. It is an alias of the service value.

type ExitResult

type ExitResult = coreproc.ExitValue

ExitResult is the outcome of a finished process — exit code, terminating signal, and resource usage. It is an alias of the core port's ExitValue.

type Limit

type Limit = coreproc.LimitValue

Limit is a soft/hard resource-limit pair for setrlimit(2). It is an alias of the core port's LimitValue.

type Module

type Module = svcself.ModuleValue

Module is one module of the running binary, with its release, its commit and its local directory told apart. It is an alias of the service value.

type Process

type Process = coreproc.Process

Process is the handle to a spawned process: PID, Wait, Signal, SignalGroup, and a group-aware Stop. It is an alias of the core port interface.

func MustStart

func MustStart(ctx context.Context, spec Spec) Process

MustStart is like Start but panics with the typed error when the spawn fails — UnsupportedPlatform off Unix/Windows, InvalidSpec, RlimitFailed, … It is the idiomatic Go MustX opt-in (like regexp.MustCompile) for a consumer that chooses crash-on-failure at its own startup; the SDK itself never panics, and Start is the non-panicking form for normal use. The panic value is the typed error, so a top-level recover() can classify it via errs.CodeOf / HasCode.

func Start

func Start(ctx context.Context, spec Spec) (proc Process, err error)

Start spawns the process described by spec and returns a live Process handle. It delegates to internal/service/proc/exec; ctx is honoured up to the fork/exec boundary. A bare Spec.Path is searched in the child's PATH (see the package documentation). On Windows the Unix-only Spec fields are refused with UnsupportedPlatform; on platforms that are neither Unix nor Windows, Start itself returns UnsupportedPlatform.

type Resource

type Resource = coreproc.Resource

Resource identifies a per-process resource governed by setrlimit(2). It is an alias of the core port type.

const (
	// ResourceNoFile limits the highest open file descriptor (RLIMIT_NOFILE).
	ResourceNoFile Resource = coreproc.ResourceNoFile
	// ResourceCore limits the size of a core dump in bytes (RLIMIT_CORE).
	ResourceCore Resource = coreproc.ResourceCore
	// ResourceCPU limits CPU time in seconds (RLIMIT_CPU).
	ResourceCPU Resource = coreproc.ResourceCPU
	// ResourceAS limits the process virtual address-space size (RLIMIT_AS).
	ResourceAS Resource = coreproc.ResourceAS
)

The resource sentinels callers most often set in Spec.Rlimits, re-exported so a Spec can be built without importing internal/core/proc. The full set lives on the core Resource type.

type Signal

type Signal = coreproc.Signal

Signal is a typed, platform-portable OS signal. It is an alias of the core port type, so process.SIGTERM and a signal parsed elsewhere compare equal.

type Spec

type Spec = coreproc.Spec

Spec is the immutable description of a process to spawn — executable, environment, credentials, isolation topology, and scheduling attributes. It is an alias of the core port type.

type Stats

type Stats = svcself.StatsValue

Stats is the running process at one instant: identity, scheduler settings, goroutines, heap, collections with their pause distribution, scheduling latency, and CPU time. Every count is cumulative since the process started. It is an alias of the service value.

func Self

func Self() Stats

Self takes a snapshot of the running process. It never fails: a figure the platform cannot give is zero, and Stats.CPUEstimated says when CPUTime is the runtime's estimate rather than the kernel's count.

type StdioMode

type StdioMode = coreproc.StdioMode

StdioMode selects how a spawned child's standard streams (stdin, stdout, stderr) are wired. It is an alias of the core port type; set it on Spec.Stdio. The zero value is StdioInherit.

const StdioCapture StdioMode = coreproc.StdioCapture

StdioCapture connects the child to Spec.Stdout/Stderr (io.Writer) and Spec.Stdin (io.Reader); a nil stream falls back to the null device for that one stream. The systemd analogue is StandardOutput=/StandardError=/StandardInput=.

const StdioInherit StdioMode = coreproc.StdioInherit

StdioInherit shares the parent's stdin/stdout/stderr with the child — the default and the only behaviour before per-process wiring existed.

const StdioNull StdioMode = coreproc.StdioNull

StdioNull connects every stream to the null device: output is discarded and the child's stdin returns EOF immediately.

Jump to

Keyboard shortcuts

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