From 0d6d1f1cbaf890a543d9dbce0cf9647be9fefbb0 Mon Sep 17 00:00:00 2001 From: Hugo Frappier Date: Mon, 14 May 2018 12:22:00 -0400 Subject: [PATCH] Add basic windows support --- cmd.go | 8 +++----- cmd_darwin.go | 20 ++++++++++++++++++++ cmd_linux.go | 20 ++++++++++++++++++++ cmd_windows.go | 20 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 cmd_darwin.go create mode 100644 cmd_linux.go create mode 100644 cmd_windows.go diff --git a/cmd.go b/cmd.go index 40a11df..400d40d 100644 --- a/cmd.go +++ b/cmd.go @@ -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 @@ -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. diff --git a/cmd_darwin.go b/cmd_darwin.go new file mode 100644 index 0000000..6d6a960 --- /dev/null +++ b/cmd_darwin.go @@ -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} +} diff --git a/cmd_linux.go b/cmd_linux.go new file mode 100644 index 0000000..6d6a960 --- /dev/null +++ b/cmd_linux.go @@ -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} +} diff --git a/cmd_windows.go b/cmd_windows.go new file mode 100644 index 0000000..63ef84f --- /dev/null +++ b/cmd_windows.go @@ -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{} +}