@@ -2,9 +2,10 @@ package trapcmd
22
33import (
44 "io"
5+ "os"
56 "os/exec"
7+ "os/signal"
68 "strings"
7- "sync"
89
910 "github.com/spf13/cobra"
1011)
@@ -15,8 +16,35 @@ type InvocationResult struct {
1516 Stderr string
1617}
1718
19+ func stream (reader io.Reader , writer io.Writer , doneChan chan <- bool , cancelChan <- chan bool ) {
20+ b := make ([]byte , 1 )
21+ for {
22+ select {
23+ case <- cancelChan :
24+ doneChan <- true
25+ return
26+ default :
27+ inN , inErr := reader .Read (b )
28+ if inN > 0 {
29+ _ , outErr := writer .Write (b )
30+ if outErr != nil {
31+ doneChan <- false
32+ return
33+ }
34+ }
35+ if inErr == io .EOF {
36+ doneChan <- true
37+ return
38+ }
39+ if inErr != nil {
40+ doneChan <- false
41+ return
42+ }
43+ }
44+ }
45+ }
46+
1847func RunWrappedCommand (trapCmd * cobra.Command , invocation []string ) (* InvocationResult , error ) {
19- var wg sync.WaitGroup
2048 cmd := exec .Command (invocation [0 ], invocation [1 :]... )
2149 cmd .Stdin = trapCmd .InOrStdin ()
2250
@@ -33,15 +61,45 @@ func RunWrappedCommand(trapCmd *cobra.Command, invocation []string) (*Invocation
3361 stdoutReader := io .TeeReader (outReader , & outBuilder )
3462 stderrReader := io .TeeReader (errReader , & errBuilder )
3563
36- wg .Add (1 )
37- go func () {
38- defer wg .Done ()
39- io .Copy (trapCmd .OutOrStdout (), stdoutReader )
40- }()
41- wg .Add (1 )
64+ outDoneChannel := make (chan bool , 1 )
65+ outCancelChannel := make (chan bool , 1 )
66+ errDoneChannel := make (chan bool , 1 )
67+ errCancelChannel := make (chan bool , 1 )
68+
69+ go stream (stdoutReader , trapCmd .OutOrStdout (), outDoneChannel , outCancelChannel )
70+ go stream (stderrReader , trapCmd .ErrOrStderr (), errDoneChannel , errCancelChannel )
71+
72+ signalsChannel := make (chan os.Signal , 1 )
73+ exitChannel := make (chan int , 1 )
74+ signal .Notify (signalsChannel , os .Interrupt , os .Kill )
75+
4276 go func () {
43- defer wg .Done ()
44- io .Copy (trapCmd .ErrOrStderr (), stderrReader )
77+ success := true
78+ completed := 0
79+ for {
80+ select {
81+ case outSuccess := <- outDoneChannel :
82+ success = success && outSuccess
83+ completed += 1
84+ case errSuccess := <- errDoneChannel :
85+ success = success && errSuccess
86+ completed += 1
87+ case <- signalsChannel :
88+ outCancelChannel <- true
89+ errCancelChannel <- true
90+ exitChannel <- 130
91+ return
92+ default :
93+ if completed == 2 {
94+ if success {
95+ exitChannel <- 0
96+ } else {
97+ exitChannel <- 1
98+ }
99+ return
100+ }
101+ }
102+ }
45103 }()
46104
47105 exitCode := 0
@@ -58,6 +116,11 @@ func RunWrappedCommand(trapCmd *cobra.Command, invocation []string) (*Invocation
58116 err = runErr
59117 }
60118 }
61- wg .Wait ()
119+
120+ coordinatorExitCode := <- exitChannel
121+ if coordinatorExitCode != 0 {
122+ exitCode = coordinatorExitCode
123+ }
124+
62125 return & InvocationResult {ExitCode : exitCode , Stdout : outBuilder .String (), Stderr : errBuilder .String ()}, err
63126}
0 commit comments