From c8224b3f9488ca80089a7084af9167690e6b7bd5 Mon Sep 17 00:00:00 2001 From: Emil Hessman Date: Sat, 24 Jan 2015 16:09:24 +0100 Subject: [PATCH] command: fix Windows build Add a per GOOS implementation to send signals to PIDs since syscall.Kill does not exist on Windows. It will always send a SIGKILL on Windows since there seems to be no POSIX compatible notion. Fixes build on Windows. --- command/lock.go | 20 ++++++++++++-------- command/util_unix.go | 12 ++++++++++++ command/util_windows.go | 19 +++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 command/util_unix.go create mode 100644 command/util_windows.go diff --git a/command/lock.go b/command/lock.go index 7deb54b46..a6ae7c34f 100644 --- a/command/lock.go +++ b/command/lock.go @@ -27,7 +27,7 @@ const ( ) // LockCommand is a Command implementation that is used to setup -// a "lock" which manages lock acquasition and invokes a sub-process +// a "lock" which manages lock acquisition and invokes a sub-process type LockCommand struct { ShutdownCh <-chan struct{} Ui cli.Ui @@ -47,6 +47,8 @@ Usage: consul lock [options] prefix child... the child process will be sent a SIGTERM signal and given time to gracefully exit. After the grace period expires the process will be hard terminated. + On Windows agents, the process is always hard terminated, even on + the first attempt. When -n=1, only a single lock holder or leader exists providing mutual exclusion. Setting a higher value switches to a semaphore @@ -286,6 +288,8 @@ func (c *LockCommand) startChild(script string, doneCh chan struct{}) error { // killChild is used to forcefully kill the child, first using SIGTERM // to allow for a graceful cleanup and then using SIGKILL for a hard // termination. +// On Windows, the child is always hard terminated with SIGKILL, even +// on the first attempt. func (c *LockCommand) killChild(childDone chan struct{}) error { // Get the child process c.childLock.Lock() @@ -300,11 +304,11 @@ func (c *LockCommand) killChild(childDone chan struct{}) error { return nil } - // Attempt a SIGTERM first + // Attempt termination first if c.verbose { - c.Ui.Info(fmt.Sprintf("Sending SIGTERM to child pid %d", child.Pid)) + c.Ui.Info(fmt.Sprintf("Terminating child pid %d", child.Pid)) } - if err := syscall.Kill(child.Pid, syscall.SIGTERM); err != nil { + if err := signalPid(child.Pid, syscall.SIGTERM); err != nil { return fmt.Errorf("Failed to terminate %d: %v", child.Pid, err) } @@ -312,7 +316,7 @@ func (c *LockCommand) killChild(childDone chan struct{}) error { select { case <-childDone: if c.verbose { - c.Ui.Info("Child exited after SIGTERM") + c.Ui.Info("Child terminated") } return nil case <-time.After(lockKillGracePeriod): @@ -322,11 +326,11 @@ func (c *LockCommand) killChild(childDone chan struct{}) error { } } - // Send a final SIGKILL first + // Send a final SIGKILL if c.verbose { - c.Ui.Info(fmt.Sprintf("Sending SIGKILL to child pid %d", child.Pid)) + c.Ui.Info(fmt.Sprintf("Killing child pid %d", child.Pid)) } - if err := syscall.Kill(child.Pid, syscall.SIGKILL); err != nil { + if err := signalPid(child.Pid, syscall.SIGKILL); err != nil { return fmt.Errorf("Failed to kill %d: %v", child.Pid, err) } return nil diff --git a/command/util_unix.go b/command/util_unix.go new file mode 100644 index 000000000..a4eda0369 --- /dev/null +++ b/command/util_unix.go @@ -0,0 +1,12 @@ +// +build !windows + +package command + +import ( + "syscall" +) + +// signalPid sends a sig signal to the process with process id pid. +func signalPid(pid int, sig syscall.Signal) error { + return syscall.Kill(pid, sig) +} diff --git a/command/util_windows.go b/command/util_windows.go new file mode 100644 index 000000000..ecdea4e02 --- /dev/null +++ b/command/util_windows.go @@ -0,0 +1,19 @@ +// +build windows + +package command + +import ( + "os" + "syscall" +) + +// signalPid sends a sig signal to the process with process id pid. +// Interrupts et al is not implemented on Windows. Always send a SIGKILL. +func signalPid(pid int, sig syscall.Signal) error { + p, err := os.FindProcess(pid) + if err != nil { + return err + } + _ = sig + return p.Signal(syscall.SIGKILL) +}