platform

package
v0.8.19 Latest Latest
Warning

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

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

Documentation

Overview

Package platform contains the small amount of process behaviour that is inherently operating-system specific. Keeping it outside the session package makes the PTY lifecycle compile cleanly on unsupported platforms.

Index

Constants

This section is empty.

Variables

View Source
var ErrShellUnsupported = errors.New("shell execution not supported on this platform")

ErrShellUnsupported reports that the current platform has no supported system shell launcher for PTY-backed sessions.

View Source
var ReplaceTimeout = 2 * time.Second

ReplaceTimeout bounds how long a replacement waits for a refusal to clear. It is a var so a test can shorten it; nothing else should write it.

Functions

func IsPTYCloseError

func IsPTYCloseError(err error) bool

IsPTYCloseError recognizes the EIO returned by many Unix PTY masters after the slave side has closed.

func KillProcessGroup

func KillProcessGroup(command *exec.Cmd)

KillProcessGroup forcefully stops the process group and then kills the leader as a fallback in case group signalling failed.

func NewShellCommand

func NewShellCommand(ctx context.Context, script string) (*exec.Cmd, error)

NewShellCommand constructs the explicitly requested Unix shell invocation. The script is passed as one argument; it is never interpolated by Relayer.

func ProcessGroupExists

func ProcessGroupExists(command *exec.Cmd) bool

ProcessGroupExists reports whether the command's process group still exists.

func ProcessGroupHasLiveMember added in v0.8.14

func ProcessGroupHasLiveMember(command *exec.Cmd) bool

ProcessGroupHasLiveMember reports whether the command's process group still has a member that can run. Unlike ProcessGroupExists it does not count a member left as a zombie: it runs no code and has closed every descriptor, the PTY slave included. It only keeps the group's number reserved until whoever adopted it reaps it, which an init that reaps on a timer takes a second or two to do, and Relayer itself running as PID 1 never does.

It is only meaningful once the group was sent SIGKILL: the kernel then lets no member start a new one, so a group seen with nothing but zombies stays that way. Only Linux can tell a zombie apart here; elsewhere, and wherever /proc may not show every member, it is ProcessGroupExists.

func ProcessGroupIDHasLiveMember added in v0.8.14

func ProcessGroupIDHasLiveMember(pgid int) bool

ProcessGroupIDHasLiveMember is ProcessGroupHasLiveMember for a group known only by its number.

func ProcessGroupLivenessIsExact added in v0.8.14

func ProcessGroupLivenessIsExact() bool

ProcessGroupLivenessIsExact reports whether ProcessGroupHasLiveMember can tell a group left only with zombies from a live one here, rather than count every group that exists as live.

func PublishByRename added in v0.8.1

func PublishByRename(source, target string) error

PublishByRename moves a fully written temporary file over the file it replaces, waiting out a transient platform refusal.

A failure that will not clear -- a missing source, a missing directory -- is returned at once: waiting does not create a file, and the caller needs the report now.

func ReapProcessGroupZombies added in v0.8.15

func ReapProcessGroupZombies(command *exec.Cmd) int

ReapProcessGroupZombies waits for every member of the command's process group that has exited and is Relayer's child, and returns how many it reaped. Only the leader starts as Relayer's child, and it is reaped by os/exec; the other members become Relayer's children only when they are orphaned and Relayer adopts them, which it does as PID 1 in a container started without an init. Nothing else would ever reap those, so each Stop would leave its agent's orphans in the process table for good.

It never blocks and never waits for a process outside the group, so it cannot take the exit status of a child os/exec is waiting for elsewhere. Call it only once the leader has been reaped, as its own Wait would otherwise lose the leader's status.

func RemoveFile added in v0.8.1

func RemoveFile(path string) error

RemoveFile deletes a file, waiting out a transient platform refusal.

A file that is already absent is not an error: callers use this to clear a path before writing it, and a path that is already clear is the state they wanted.

func RetryWhileTransient added in v0.8.1

func RetryWhileTransient(operation func() error) error

RetryWhileTransient runs an operation until it succeeds, fails permanently, or the budget is spent. The last error is returned unwrapped so a caller can still inspect it.

func SetGracefulCancel added in v0.8.6

func SetGracefulCancel(command *exec.Cmd, grace time.Duration, stop func())

SetGracefulCancel makes cancelling the command's context call stop — the caller's graceful stop request — instead of os/exec's default of killing the leader outright. A leader still running after grace is then killed by os/exec itself.

Without it, every path that cancels a parent context before stopping the sessions — a supervisor shutting down, a signal to relayer serve — sent SIGKILL to each agent before any SIGTERM, so no agent could shut down cleanly. The caller passes its own stop request rather than a signal, so the cancellation and a later explicit stop are one request and one SIGTERM: an agent whose handler runs only once was killed by a second.

os/exec may call Cancel after the leader was reaped but before Wait returned, and the session only learns of the reap when Wait returns. In that moment the stop request signals the group by number, as the session's own cleanup does right after the reap. That is safe on Unix: the kernel keeps a group's number reserved while any member lives, and a group with no member left answers ESRCH unless the whole PID space wrapped around in between. Windows has no such rule, which is why it holds the process handle instead.

func TerminateProcessGroup

func TerminateProcessGroup(command *exec.Cmd)

TerminateProcessGroup asks the whole process group led by command to stop. creack/pty starts commands in a new session on Unix, so the command PID is also the process-group ID.

func TransientFileError added in v0.8.1

func TransientFileError(error) bool

TransientFileError reports nothing as transient away from Windows.

rename(2) and unlink(2) do not fail because another process holds the file open, so every error they return is a real one: a missing path, a cross-device move, a permission problem. Retrying would delay the report without changing it.

Types

This section is empty.

Jump to

Keyboard shortcuts

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