Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command is still executed if Stop() is called during BeforeExec hooks #94

Open
renard opened this issue Sep 13, 2022 · 0 comments
Open

Comments

@renard
Copy link

renard commented Sep 13, 2022

If for some reason a BeforeExec takes some time (to delay a command for example) and Stop() is called while BeforeExec hasn't finished, the command is still executed.

Example:

package main

import (
	"fmt"
	"os/exec"
	"time"

	"github.com/go-cmd/cmd"
)

func main() {
	cmdOptions := cmd.Options{
		Buffered:  true,
		Streaming: true,
		BeforeExec: []func(_c *exec.Cmd){
			func(_c *exec.Cmd) {
				for i := 0; i < 4; i++ {
					fmt.Printf("Waiting %d\n", i)
					time.Sleep(1 * time.Second)
				}
			},
		},
	}

	c := cmd.NewCmdOptions(cmdOptions, "ls", "-l")

	fmt.Printf("Starting\n")
	c.Start()
	time.Sleep(2 * time.Second)

	fmt.Printf("Stopping\n")
	err := c.Stop()
	if err != nil {
		fmt.Printf("E: %s\n", err)
	}

	fmt.Printf("Waiting process to end\n")
	<-c.Done()
	fmt.Printf("Done\n")
	fmt.Printf("Output: %s\n", c.Status().Stdout)
}

Output:

Starting
Waiting 0
Waiting 1
Waiting 2
Stopping
E: command not running
Waiting process to end
Waiting 3
Done
Output: [total 8 -rw-r--r--  1 renard  staff  612 Sep 13 22:24 main.go]

Stop() has no effect if command is not started because of

cmd/cmd.go

Lines 290 to 297 in fa11d77

// c.statusChan is created in StartWithStdin()/Start(), so if nil the caller
// hasn't started the command yet. c.started is set true in run() only after
// the underlying os/exec.Cmd.Start() has returned without an error, so we're
// sure the command has started (although it might exit immediately after,
// we at least know it started).
if c.statusChan == nil || !c.started {
return ErrNotStarted
}

run() runs all BeforeExec functions and starts the command (

cmd/cmd.go

Lines 431 to 448 in fa11d77

// Run all optional commands to customize underlying os/exe.Cmd.
for _, f := range c.beforeExecFuncs {
f(cmd)
}
// //////////////////////////////////////////////////////////////////////
// Start command
// //////////////////////////////////////////////////////////////////////
now := time.Now()
if err := cmd.Start(); err != nil {
c.Lock()
c.status.Error = err
c.status.StartTs = now.UnixNano()
c.status.StopTs = time.Now().UnixNano()
c.done = true
c.Unlock()
return
}
).

Would it make sense to add a ForceStop() function like:

// ForceStop forces a command stop by calling Stop(). If the command hasn't
// started yet flags it as stopped to prevent Start() from starting it.
//
// See Stop() for further details
func (c *Cmd) ForceStop() error {
	err := c.Stop()
	// If command hasn't started, return error (or nil) from Stop()
	if err != ErrNotStarted {
		return err
	}
	// flag command as stopped to prevent it from being started later.
	c.stopped = true
	return nil
}

Thanks in advance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant