diff --git a/cmd/go-timeout/main.go b/cmd/go-timeout/main.go index 9e6de5c..2d3b5bf 100644 --- a/cmd/go-timeout/main.go +++ b/cmd/go-timeout/main.go @@ -13,9 +13,10 @@ import ( ) func main() { - optKillAfter := getopt.StringLong("kill-after", 'k', "", "help message for f") - optSig := getopt.StringLong("signal", 's', "", "help message for long") - p := getopt.BoolLong("preserve-status", 0, "help message for bool") + optKillAfter := getopt.StringLong("kill-after", 'k', "", "also send a KILL signal if COMMAND is still running. this long after the initial signal was sent") + optSig := getopt.StringLong("signal", 's', "", "specify the signal to be sent on timeout. IGNAL may be a name like 'HUP' or a number. see 'kill -l' for a list of signals") + optForeground := getopt.BoolLong("foreground", 0, "when not running timeout directly from a shell prompt, allow COMMAND to read from the TTY and get TTY signals. in this mode, children of COMMAND will not be timed out") + p := getopt.BoolLong("preserve-status", 0, "exit with the same status as COMMAND, even when the command times out") opts := getopt.CommandLine opts.Parse(os.Args) @@ -54,10 +55,11 @@ func main() { cmd := exec.Command(rest[1], rest[2:]...) tio := &timeout.Timeout{ - Duration: time.Duration(dur * float64(time.Second)), - Cmd: cmd, - KillAfter: time.Duration(killAfter * float64(time.Second)), - Signal: sig, + Duration: time.Duration(dur * float64(time.Second)), + Cmd: cmd, + Foreground: *optForeground, + KillAfter: time.Duration(killAfter * float64(time.Second)), + Signal: sig, } exit := tio.RunSimple(*p) os.Exit(exit) diff --git a/timeout.go b/timeout.go index c9c0d47..480819a 100644 --- a/timeout.go +++ b/timeout.go @@ -15,10 +15,11 @@ import ( // Timeout is main struct of timeout package type Timeout struct { - Duration time.Duration - KillAfter time.Duration - Signal os.Signal - Cmd *exec.Cmd + Duration time.Duration + KillAfter time.Duration + Signal os.Signal + Foreground bool + Cmd *exec.Cmd } var defaultSignal os.Signal @@ -206,7 +207,7 @@ func (tio *Timeout) handleTimeout() (ex ExitStatus) { ex.typ = exitTypeNormal return ex case <-time.After(tio.Duration): - cmd.Process.Signal(tio.signal()) // XXX error handling + tio.terminate() ex.typ = exitTypeTimedOut } diff --git a/timeout_unix.go b/timeout_unix.go index 460c3c3..5844008 100644 --- a/timeout_unix.go +++ b/timeout_unix.go @@ -14,6 +14,14 @@ func (tio *Timeout) getCmd() *exec.Cmd { return tio.Cmd } +func (tio *Timeout) terminate() error { + syssig, ok := tio.signal().(syscall.Signal) + if !ok || tio.Foreground { + return tio.Cmd.Process.Signal(tio.signal()) + } + return syscall.Kill(-tio.Cmd.Process.Pid, syssig) +} + func (tio *Timeout) killall() error { return syscall.Kill(-tio.Cmd.Process.Pid, syscall.SIGKILL) } diff --git a/timeout_windows.go b/timeout_windows.go index 12b1a3c..ab8ee6b 100644 --- a/timeout_windows.go +++ b/timeout_windows.go @@ -7,7 +7,7 @@ import ( ) func (tio *Timeout) getCmd() *exec.Cmd { - if tio.Cmd.SysProcAttr == nil { + if !tio.Foreground && tio.Cmd.SysProcAttr == nil { tio.Cmd.SysProcAttr = &syscall.SysProcAttr{ CreationFlags: syscall.CREATE_UNICODE_ENVIRONMENT | 0x00000200, } @@ -15,6 +15,10 @@ func (tio *Timeout) getCmd() *exec.Cmd { return tio.Cmd } +func (tio *Timeout) terminate() error { + return tio.Cmd.Process.Signal(tio.signal()) +} + func (tio *Timeout) killall() error { return exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(tio.Cmd.Process.Pid)).Run() }