Process manager for Go — start, monitor, and stop subprocesses with graceful shutdown, process group management, and PID-based state persistence.
import "github.com/stnmrshx/go-processmanager"
p := process.New(
process.WithName("/bin/bash"),
process.WithArgs("-c", "while true; do echo hello; sleep 1; done"),
process.WithTemporaryStateDir(),
)
if err := p.Run(); err != nil {
log.Fatal(err)
}
defer p.Stop()
// Wait for the process to exit or call p.IsAlive() / p.ExitCode()
<-p.Done()- Graceful shutdown — SIGTERM with configurable timeout, then SIGKILL if needed (default 15s). Default kill signal is SIGKILL (9), customizable via
WithKillSignal(). - Process group killing — by default the entire process group is targeted (negative PID on Unix). Disable with
WithKillProcessGroup(false). - PID-based state — all process metadata (PID, stdout, stderr, exit code, errors) is stored in a state directory (
StateDir). Recreate aProcesspointing at the same dir to attach to an existing one. - Zombie reaping — on Linux, calls
PR_SET_CHILD_SUBREAPERso orphaned child processes are cleaned up. No-op on other platforms. - Subprocess stdio — stdout and stderr are appended to files in the state directory via
NewLog(). Stdin can be wired viaWithSTDIN().
config.go — Config struct, DefaultConfig(), Option type
options.go — Functional options (WithName, WithArgs, WithStateDir, etc.)
process.go — Core Process: Run(), Stop(), IsAlive(), Done(), ExitCode()
writer.go — NewLog(path) opens an append-mode log file
process_unix.go — Unix build tag: new process group, negative PID kill, zombie reaping
process_windows.go — Windows build tag: direct Kill via os.FindProcess, no-op reaping
subreaper_linux.go — Linux-specific SetSubreaper() via unix.Prctl(PR_SET_CHILD_SUBREAPER)
subreaper_other.go — No-op SetSubreaper() on non-Linux
| Function / Method | Purpose |
|---|---|
process.New(opts...) |
Create a new Process with functional options |
p.Run() |
Start the subprocess; returns error if already running |
p.Stop() |
Send KillSignal (SIGTERM by default), wait GracefulTimeout, then SIGKILL |
p.IsAlive() |
Check if process is still running via PID |
p.Done() |
Channel closed when the process exits |
p.ExitCode() |
Read the exit code from state directory |
p.StateDir() |
Return the path to the state directory |
p.StdoutPath() / p.StderrPath() |
Paths to stdout/stderr log files |
| Option | Purpose |
|---|---|
WithName(string) |
Executable path or name |
WithArgs(...string) |
Command-line arguments (appended) |
WithStateDir(string) |
Explicit state directory path |
WithTemporaryStateDir() |
Create a temporary state directory |
WithWorkDir(string) |
Working directory for the subprocess |
WithEnvironment(...string) |
Environment variables (replaces os.Environ()) |
WithSTDIN(*os.File) |
Stdin file handle |
WithKillSignal(int) |
Kill signal number (default 9 / SIGKILL) |
WithGracefulTimeout(time.Duration) |
Timeout after SIGTERM before SIGKILL (default 15s) |
WithKillProcessGroup(bool) |
Target entire process group (default true) |
go test ./...Tests use Ginkgo / Gomega with dot-imports. Several specs use Eventually() with 2m timeouts.
| Tag | Effect |
|---|---|
unix |
Process groups, negative PID kill, zombie reaping |
windows |
Direct Kill via os.FindProcess, no-op reaping |
linux |
PR_SET_CHILD_SUBREAPER subreaper |
!linux |
No-op SetSubreaper |
MIT License 2026 StnMrshx — see LICENSE.