proc

package
v0.2026215.2227 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: MIT Imports: 11 Imported by: 13

Documentation

Overview

Package proc is a stdlib-only fabric slice of the spec contract module (github.com/opencharly/spec/proc, #55 step1) holding generic host-process lifecycle + launch plumbing: catchable-signal-triggered cleanup hooks, a stale-temp-file sweep, the current-binary subcommand runner, and the process-group shutdown + remote-launch-command renderers (launch.go). It is a host primitive a plugin physically cannot hold as a shared library, so by #55 Rule 1 it lives in a contained spec slice (deps: os/os-signal/os-exec + spec value types + spec.ShellQuote — stdlib-weight only, Rule-2-free) that charly core AND every plugin can import directly, without pulling any mechanism kit. Relocated here from sdk/proclifecycle (cleanup + run) and sdk/kit/process.go (the launch helpers) — a genuinely shared leaf.

Index

Constants

View Source
const ProcessShutdownGrace = 2 * time.Second

ProcessShutdownGrace bounds each stage of the graceful process-group shutdown (ShutdownProcessGroup). Named + documented, not a tuned magic value: it is the guarantee that closing a stdio-carried process ALWAYS terminates instead of wedging the caller on a child that ignores its stdin EOF.

Variables

View Source
var RunCharlySubcommand = func(args ...string) error {
	exe := os.Args[0]
	cmd := exec.Command(exe, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

RunCharlySubcommand shells out to the CURRENT binary (os.Args[0]) with args, inheriting stdin/stdout/stderr. Relocated from charly core (run_subcommand.go) as a stdlib-only host-exec leaf: the host-side update/deploy orchestration (podUpdateCmd, the unified-target Update/Rebuild methods, per-kind R10 sequences, `charly vm cycle`) spawns child `charly <args…>` invocations through it, and using os.Args[0] means an update loop picks up the local build-under-test automatically.

A package var (not a plain func) so tests can stub the child-process boundary (e.g. record the image-build / vm-cp-box calls a deploy makes without actually spawning the binary).

Functions

func InstallSignalHandler

func InstallSignalHandler()

InstallSignalHandler arms the global signal handler exactly once. Subsequent calls are no-ops. On SIGTERM/SIGINT/SIGHUP it runs the registered temp cleanups AND the registered shutdown hooks (plugin-client reaping, etc.), then re-raises the signal so the process exits with the conventional 128+signum status code.

SIGKILL is not catchable (kernel-level). Leftover temps from a SIGKILL'd charly are caught by the next invocation's SweepStaleTemps; orphaned plugin servers from a SIGKILL'd charly self-terminate via the plugin SDK's parent-death watch (see plugin/sdk/parentwatch.go).

func RegisterShutdownHook

func RegisterShutdownHook(fn func())

RegisterShutdownHook registers fn to run on a catchable shutdown signal (SIGTERM/SIGINT/SIGHUP), in the signal handler, before it re-raises the signal and the process exits. It is the package-boundary seam that lets package main reap resources this package's signal handler cannot reference directly — notably charly's connected out-of-process plugin clients (providerRegistry.Close): a Ctrl-C'd / `systemctl stop`ped charly kills its plugin servers instead of orphaning them (the 77h-orphan leak). Hooks run in registration order; each MUST be best-effort and bounded (the handler is synchronous). A nil fn is a no-op.

func RegisterTempCleanup

func RegisterTempCleanup(path string)

RegisterTempCleanup registers path for cleanup on graceful shutdown (SIGTERM/SIGINT/SIGHUP). Existing `defer os.Remove(...)` callers keep working for normal exit; this is a SUPPLEMENTAL safety net for the ungraceful-but-catchable signal path. Empty path is a no-op.

func RemoteLaunchCommand

func RemoteLaunchCommand(launch spec.ProcessLaunch) string

RemoteLaunchCommand renders one exact-argv launch as the single command string OpenSSH must receive: POSIX single-quoting each token preserves the argv boundary and prevents interpolation by the remote login shell. The working directory and environment belong to the TARGET side, so they ride inside the remote command (`cd <dir> && env <K=V>… <argv>`); applying them to the local ssh carrier would conflate controller and target state.

func ShutdownProcessGroup

func ShutdownProcessGroup(cmd *exec.Cmd, stdin io.Closer, done <-chan struct{})

ShutdownProcessGroup stops one Setpgid child process gracefully-first and reaps it. The escalation ladder is:

  1. close stdin — the polite EOF signal every stdio-carried protocol answers by exiting on its own;
  2. one ProcessShutdownGrace for the process to exit;
  3. SIGTERM to the whole process group — the explicit termination request, so a well-behaved child can still shut down cleanly;
  4. a second ProcessShutdownGrace;
  5. only then SIGKILL to the group — the final escalation a child cannot ignore — and reap.

The ladder never opens with a force-kill: SIGKILL orphans nothing (the whole group dies), but it also gives no child the chance to flush protocol state, so it stays the last resort.

done must be closed by the caller's cmd.Wait() monitor once the process is reaped; ShutdownProcessGroup returns only after that, so no zombie or running group member survives the call. Call it once per process — wrap in sync.Once when several paths may close the same process.

func SortedEnvPairs

func SortedEnvPairs(env map[string]string) []string

SortedEnvPairs renders an environment map as a deterministically ordered "KEY=value" slice. Sorting makes launched-process environments and rendered remote commands byte-reproducible across runs (map iteration order is not).

func SweepStaleTemps

func SweepStaleTemps()

SweepStaleTemps removes /tmp/<prefix>* entries that are (a) NOT held open by any running process AND (b) older than sweepSafetyFloor AND (c) owned by the current user. Best-effort: never errors, never blocks. Called once from main().

func UnregisterTempCleanup

func UnregisterTempCleanup(path string)

UnregisterTempCleanup removes a path from the registry. Called by the existing defer-cleanup paths after they successfully os.Remove the file, so the registry doesn't accumulate stale entries in long- running charly processes (e.g. `charly mcp serve`).

Types

This section is empty.

Jump to

Keyboard shortcuts

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