@@ -7,11 +7,11 @@ package subprocess
77
88import (
99 "context"
10- "fmt"
1110 "os/exec"
1211 "time"
1312
1413 "github.com/sasha-s/go-deadlock"
14+ "go.uber.org/atomic"
1515
1616 "github.com/ARM-software/golang-utils/utils/commonerrors"
1717 "github.com/ARM-software/golang-utils/utils/logs"
@@ -20,6 +20,8 @@ import (
2020 commandUtils "github.com/ARM-software/golang-utils/utils/subprocess/command"
2121)
2222
23+ const subprocessTerminationGracePeriod = 10 * time .Millisecond
24+
2325// INTERNAL
2426// wrapper over an exec cmd.
2527type cmdWrapper struct {
@@ -45,7 +47,7 @@ func (c *cmdWrapper) Start() error {
4547 c .mu .RLock ()
4648 defer c .mu .RUnlock ()
4749 if c .cmd == nil {
48- return fmt . Errorf ( "%w:undefined command", commonerrors . ErrUndefined )
50+ return commonerrors . UndefinedVariable ( " command" )
4951 }
5052 return ConvertCommandError (c .cmd .Start ())
5153}
@@ -54,45 +56,31 @@ func (c *cmdWrapper) Run() error {
5456 c .mu .RLock ()
5557 defer c .mu .RUnlock ()
5658 if c .cmd == nil {
57- return fmt . Errorf ( "%w:undefined command", commonerrors . ErrUndefined )
59+ return commonerrors . UndefinedVariable ( " command" )
5860 }
5961 return ConvertCommandError (c .cmd .Run ())
6062}
6163
62- type interruptType int
63-
64- const (
65- sigint interruptType = 2
66- sigkill interruptType = 9
67- sigterm interruptType = 15
68- )
69-
70- func (c * cmdWrapper ) interruptWithContext (ctx context.Context , interrupt interruptType ) error {
64+ func (c * cmdWrapper ) interruptWithContext (ctx context.Context , interrupt proc.InterruptType ) error {
7165 c .mu .RLock ()
7266 defer c .mu .RUnlock ()
7367 if c .cmd == nil {
74- return commonerrors .New ( commonerrors . ErrUndefined , "undefined command" )
68+ return commonerrors .UndefinedVariable ( " command" )
7569 }
7670 subprocess := c .cmd .Process
7771 ctx , cancel := context .WithCancel (ctx )
7872 defer cancel ()
79- var stopErr error
73+ stopErr := atomic . NewError ( nil )
8074 if subprocess != nil {
8175 pid := subprocess .Pid
82- parallelisation .ScheduleAfter (ctx , 10 * time . Millisecond , func (time.Time ) {
83- process , err := proc .FindProcess (ctx , pid )
84- if process == nil || err != nil {
76+ parallelisation .ScheduleAfter (ctx , subprocessTerminationGracePeriod , func (time.Time ) {
77+ process , sErr := proc .FindProcess (ctx , pid )
78+ if process == nil || sErr != nil {
8579 return
8680 }
87- switch interrupt {
88- case sigint :
89- _ = process .Interrupt (ctx )
90- case sigkill :
91- _ = process .KillWithChildren (ctx )
92- case sigterm :
93- _ = process .Terminate (ctx )
94- default :
95- stopErr = commonerrors .New (commonerrors .ErrInvalid , "unknown interrupt type for process" )
81+ sErr = proc .InterruptProcess (ctx , pid , interrupt )
82+ if commonerrors .Any (sErr , commonerrors .ErrInvalid , commonerrors .ErrCancelled , commonerrors .ErrTimeout ) {
83+ stopErr .Store (sErr )
9684 }
9785 })
9886 }
@@ -102,31 +90,31 @@ func (c *cmdWrapper) interruptWithContext(ctx context.Context, interrupt interru
10290 return err
10391 }
10492
105- return stopErr
93+ return stopErr . Load ()
10694}
10795
108- func (c * cmdWrapper ) interrupt (interrupt interruptType ) error {
96+ func (c * cmdWrapper ) interrupt (interrupt proc. InterruptType ) error {
10997 return c .interruptWithContext (context .Background (), interrupt )
11098}
11199
112100func (c * cmdWrapper ) Stop () error {
113- return c .interrupt (sigkill )
101+ return c .interrupt (proc . SigKill )
114102}
115103
116104func (c * cmdWrapper ) Interrupt (ctx context.Context ) error {
117- return c .interruptWithContext (ctx , sigint )
105+ return c .interruptWithContext (ctx , proc . SigInt )
118106}
119107
120108func (c * cmdWrapper ) Pid () (pid int , err error ) {
121109 c .mu .RLock ()
122110 defer c .mu .RUnlock ()
123111 if c .cmd == nil {
124- err = fmt . Errorf ( "%w:undefined command", commonerrors . ErrUndefined )
112+ err = commonerrors . UndefinedVariable ( " command" )
125113 return
126114 }
127115 subprocess := c .cmd .Process
128116 if subprocess == nil {
129- err = fmt . Errorf ( "%w:undefined subprocess", commonerrors . ErrUndefined )
117+ err = commonerrors . UndefinedVariable ( " subprocess" )
130118 return
131119 }
132120 pid = subprocess .Pid
@@ -146,6 +134,13 @@ type command struct {
146134func (c * command ) createCommand (cmdCtx context.Context ) * exec.Cmd {
147135 newCmd , newArgs := c .as .Redefine (c .cmd , c .args ... )
148136 cmd := exec .CommandContext (cmdCtx , newCmd , newArgs ... ) //nolint:gosec
137+ cmd .Cancel = func () error {
138+ p := cmd .Process
139+ if p == nil {
140+ return nil
141+ }
142+ return proc .TerminateGracefully (context .Background (), p .Pid , subprocessTerminationGracePeriod )
143+ }
149144 cmd .Stdout = newOutStreamer (cmdCtx , c .loggers )
150145 cmd .Stderr = newErrLogStreamer (cmdCtx , c .loggers )
151146 cmd .Env = cmd .Environ ()
@@ -169,11 +164,11 @@ func (c *command) Reset() {
169164
170165func (c * command ) Check () (err error ) {
171166 if c .cmd == "" {
172- err = fmt . Errorf ( "missing command: %w" , commonerrors . ErrUndefined )
167+ err = commonerrors . UndefinedVariable ( " command" )
173168 return
174169 }
175170 if c .as == nil {
176- err = fmt . Errorf ( "missing command translator: %w" , commonerrors . ErrUndefined )
171+ err = commonerrors . UndefinedVariable ( " command translator" )
177172 return
178173 }
179174 if c .loggers == nil {
@@ -199,6 +194,7 @@ func ConvertCommandError(err error) error {
199194 return proc .ConvertProcessError (err )
200195}
201196
197+ // CleanKillOfCommand tries to terminate a command gracefully.
202198func CleanKillOfCommand (ctx context.Context , cmd * exec.Cmd ) (err error ) {
203199 if cmd == nil {
204200 return
@@ -212,13 +208,7 @@ func CleanKillOfCommand(ctx context.Context, cmd *exec.Cmd) (err error) {
212208 thisP := cmd .Process
213209 if thisP == nil {
214210 return
215- } else {
216- p , subErr := proc .FindProcess (ctx , thisP .Pid )
217- if subErr != nil {
218- err = subErr
219- return
220- }
221- err = p .KillWithChildren (ctx )
222211 }
212+ err = proc .TerminateGracefully (ctx , thisP .Pid , subprocessTerminationGracePeriod )
223213 return
224214}
0 commit comments