From 21fc32279704455c9e7c623de675745a92ebc075 Mon Sep 17 00:00:00 2001 From: Songmu Date: Sat, 25 Mar 2017 16:08:26 +0900 Subject: [PATCH 1/6] add --foreground, but not implemented --- cmd/go-timeout/main.go | 14 ++++++++------ timeout.go | 9 +++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/go-timeout/main.go b/cmd/go-timeout/main.go index 9e6de5c..162dcec 100644 --- a/cmd/go-timeout/main.go +++ b/cmd/go-timeout/main.go @@ -13,8 +13,9 @@ import ( ) func main() { - optKillAfter := getopt.StringLong("kill-after", 'k', "", "help message for f") - optSig := getopt.StringLong("signal", 's', "", "help message for long") + 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", 'f', "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, "help message for bool") opts := getopt.CommandLine @@ -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..84cbcac 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 From 5215825669423595eb6fbb0bdf1f1423f5e66f89 Mon Sep 17 00:00:00 2001 From: Songmu Date: Sat, 25 Mar 2017 16:17:28 +0900 Subject: [PATCH 2/6] define tio.terminate() --- timeout.go | 2 +- timeout_unix.go | 4 ++++ timeout_windows.go | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/timeout.go b/timeout.go index 84cbcac..480819a 100644 --- a/timeout.go +++ b/timeout.go @@ -207,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..932e7f1 100644 --- a/timeout_unix.go +++ b/timeout_unix.go @@ -14,6 +14,10 @@ func (tio *Timeout) getCmd() *exec.Cmd { return tio.Cmd } +func (tio *Timeout) terminate() error { + return cmd.Process.Signal(tio.signal()) +} + 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..2072495 100644 --- a/timeout_windows.go +++ b/timeout_windows.go @@ -15,6 +15,10 @@ func (tio *Timeout) getCmd() *exec.Cmd { return tio.Cmd } +func (tio *Timeout) terminate() error { + return cmd.Process.Signal(tio.signal()) +} + func (tio *Timeout) killall() error { return exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(tio.Cmd.Process.Pid)).Run() } From 8923c8231ce46b5ad2c302ea721ec304349f5082 Mon Sep 17 00:00:00 2001 From: Songmu Date: Sat, 25 Mar 2017 16:35:38 +0900 Subject: [PATCH 3/6] [incompatible] If `Foreground` is false, stop process group including child process. When `Foreground` is true, only that process is stopped. --- timeout_unix.go | 8 ++++++-- timeout_windows.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/timeout_unix.go b/timeout_unix.go index 932e7f1..a16142e 100644 --- a/timeout_unix.go +++ b/timeout_unix.go @@ -8,14 +8,18 @@ import ( ) func (tio *Timeout) getCmd() *exec.Cmd { - if tio.Cmd.SysProcAttr == nil { + if !tio.Foreground && tio.Cmd.SysProcAttr == nil { tio.Cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} } return tio.Cmd } func (tio *Timeout) terminate() error { - return cmd.Process.Signal(tio.signal()) + 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 { diff --git a/timeout_windows.go b/timeout_windows.go index 2072495..33a8143 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, } From 517fff103abc7d0e88a663609515d8bb55f6535d Mon Sep 17 00:00:00 2001 From: Songmu Date: Sat, 25 Mar 2017 16:41:58 +0900 Subject: [PATCH 4/6] fix compilation error --- timeout_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timeout_windows.go b/timeout_windows.go index 33a8143..ab8ee6b 100644 --- a/timeout_windows.go +++ b/timeout_windows.go @@ -16,7 +16,7 @@ func (tio *Timeout) getCmd() *exec.Cmd { } func (tio *Timeout) terminate() error { - return cmd.Process.Signal(tio.signal()) + return tio.Cmd.Process.Signal(tio.signal()) } func (tio *Timeout) killall() error { From dca652ce9b08eecbda895905129e91572e8346fb Mon Sep 17 00:00:00 2001 From: Songmu Date: Sat, 25 Mar 2017 16:53:27 +0900 Subject: [PATCH 5/6] adjsut CLI interface --- cmd/go-timeout/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/go-timeout/main.go b/cmd/go-timeout/main.go index 162dcec..2d3b5bf 100644 --- a/cmd/go-timeout/main.go +++ b/cmd/go-timeout/main.go @@ -15,8 +15,8 @@ import ( func main() { 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", 'f', "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, "help message for bool") + 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) From 2691762d8c0387b5e0c5c571105370e4bc9cb935 Mon Sep 17 00:00:00 2001 From: Songmu Date: Sat, 25 Mar 2017 17:29:03 +0900 Subject: [PATCH 6/6] always create process group on linux --- timeout_unix.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timeout_unix.go b/timeout_unix.go index a16142e..5844008 100644 --- a/timeout_unix.go +++ b/timeout_unix.go @@ -8,7 +8,7 @@ import ( ) func (tio *Timeout) getCmd() *exec.Cmd { - if !tio.Foreground && tio.Cmd.SysProcAttr == nil { + if tio.Cmd.SysProcAttr == nil { tio.Cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} } return tio.Cmd