mirror of
https://github.com/charmbracelet/crush.git
synced 2026-05-30 18:47:33 +00:00
49 lines
1.4 KiB
Go
49 lines
1.4 KiB
Go
//go:build unix
|
|
|
|
package shell
|
|
|
|
import (
|
|
"os/exec"
|
|
"syscall"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// killTimeout is how long to wait after sending SIGINT before sending
|
|
// SIGKILL when cancelling an external command. Matches mvdan.cc/sh's
|
|
// DefaultExecHandler default so users see consistent grace periods
|
|
// regardless of which path executed their command.
|
|
const killTimeout = 2 * time.Second
|
|
|
|
// prepareCmd configures cmd to run in its own process group so that on
|
|
// cancellation we can signal the entire descendant tree, not just the
|
|
// direct child. Without this, a child that fork+execs an editor (git
|
|
// spawning vim, npm spawning a dev server, etc.) leaves the grandchild
|
|
// reparented to PID 1 when we kill the child.
|
|
func prepareCmd(cmd *exec.Cmd) {
|
|
if cmd.SysProcAttr == nil {
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
}
|
|
cmd.SysProcAttr.Setpgid = true
|
|
}
|
|
|
|
// interruptCmd sends SIGINT to the entire process group. The negative
|
|
// PID is the kernel's way of saying "this PGID, not this PID". Safe to
|
|
// call after Start; a no-op if the process is already reaped.
|
|
func interruptCmd(cmd *exec.Cmd) error {
|
|
if cmd.Process == nil {
|
|
return nil
|
|
}
|
|
return unix.Kill(-cmd.Process.Pid, unix.SIGINT)
|
|
}
|
|
|
|
// killCmd sends SIGKILL to the entire process group. Same negative-PID
|
|
// convention as interruptCmd.
|
|
func killCmd(cmd *exec.Cmd) error {
|
|
if cmd.Process == nil {
|
|
return nil
|
|
}
|
|
return unix.Kill(-cmd.Process.Pid, unix.SIGKILL)
|
|
}
|