//go:build linux || darwin package subprocess import ( "errors" "os" "os/exec" "syscall" ) type unixProcessTree struct { processGroupID int } func newOwnedProcessTree() (ownedProcessTree, error) { return &unixProcessTree{}, nil } func (tree *unixProcessTree) Start(cmd *exec.Cmd) error { cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} if err := cmd.Start(); err != nil { return err } tree.processGroupID = cmd.Process.Pid return nil } func (tree *unixProcessTree) TerminateGracefully() error { return tree.signal(syscall.SIGTERM) } func (tree *unixProcessTree) TerminateForcefully() error { return tree.signal(syscall.SIGKILL) } func (tree *unixProcessTree) Close() error { return nil } func (tree *unixProcessTree) signal(signal syscall.Signal) error { if tree.processGroupID <= 0 { return nil } err := syscall.Kill(-tree.processGroupID, signal) if errors.Is(err, syscall.ESRCH) || errors.Is(err, os.ErrProcessDone) { return nil } return err }