Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (c *Cmd) Stop() error {
// Signal the process group (-pid), not just the process, so that the process
// and all its children are signaled. Else, child procs can keep running and
// keep the stdout/stderr fd open and cause cmd.Wait to hang.
return syscall.Kill(-c.status.PID, syscall.SIGTERM)
return terminateProcess(c.status.PID)
}

// Status returns the Status of the command at any time. It is safe to call
Expand Down Expand Up @@ -273,10 +273,8 @@ func (c *Cmd) run() {
// //////////////////////////////////////////////////////////////////////
cmd := exec.Command(c.Name, c.Args...)

// Set process group ID so the cmd and all its children become a new
// process group. This allows Stop to SIGTERM the cmd's process group
// without killing this process (i.e. this code here).
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
// Platform-specific SysProcAttr management
setProcessGroupID(cmd)

// Write stdout and stderr to buffers that are safe to read while writing
// and don't cause a race condition.
Expand Down
20 changes: 20 additions & 0 deletions cmd_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"os/exec"
"syscall"
)

func terminateProcess(pid int) error {
// Signal the process group (-pid), not just the process, so that the process
// and all its children are signaled. Else, child procs can keep running and
// keep the stdout/stderr fd open and cause cmd.Wait to hang.
return syscall.Kill(-pid, syscall.SIGTERM)
}

func setProcessGroupID(cmd *exec.Cmd) {
// Set process group ID so the cmd and all its children become a new
// process group. This allows Stop to SIGTERM the cmd's process group
// without killing this process (i.e. this code here).
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}
20 changes: 20 additions & 0 deletions cmd_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"os/exec"
"syscall"
)

func terminateProcess(pid int) error {
// Signal the process group (-pid), not just the process, so that the process
// and all its children are signaled. Else, child procs can keep running and
// keep the stdout/stderr fd open and cause cmd.Wait to hang.
return syscall.Kill(-pid, syscall.SIGTERM)
}

func setProcessGroupID(cmd *exec.Cmd) {
// Set process group ID so the cmd and all its children become a new
// process group. This allows Stop to SIGTERM the cmd's process group
// without killing this process (i.e. this code here).
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}
20 changes: 20 additions & 0 deletions cmd_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"fmt"
"os/exec"
"syscall"
)

// Stop stops the command by sending its process group a SIGTERM signal.
// Stop is idempotent. An error should only be returned in the rare case that
// Stop is called immediately after the command ends but before Start can
// update its internal state.
func terminateProcess(pid int) error {
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", fmt.Sprintf("%d", pid))
return kill.Run()
}

func setProcessGroupID(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}