Skip to content

Commit

Permalink
Expose exec.Cmd to caller
Browse files Browse the repository at this point in the history
Added NewExpectProc that returns exec.Cmd so the caller can directly
interact with the process
Took the opportunity to clean up the NewExpect code
  • Loading branch information
leemcloughlin committed Oct 31, 2016
1 parent 878f7e2 commit 39b84f8
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 16 deletions.
47 changes: 31 additions & 16 deletions expect.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,38 +143,55 @@ func debugf(format string, args ...interface{}) {
// NewExpect starts prog, passing any given args, in its own pty.
// Note that in order to be non-blocking while reading from the pty this sets
// the non-blocking flag and looks for EAGAIN on reads failing. This has only
// been tested on Linux systems
// been tested on Linux systems.
// On prog exiting or being killed Result is filled in shortly after.
func NewExpect(prog string, arg ...string) (*Expect, error) {
return newExpectCommon(true, prog, arg...)
}

// NewExpectProc is similar to NewExpect except the created cmd is returned.
// It is expected that the cmd will exit normally otherwise it is left to
// the caller to kill it.
// In the event of an error starting the cmd it will be killed but not reaped.
// However the cmd ends it is important that the caller reap the process
// by calling cmd.Process.Wait() otherwise it can use up a process slot in
// the operating system.
// Note that Result is not filled in.
func NewExpectProc(prog string, arg ...string) (*Expect, *exec.Cmd, error) {
exp, err := newExpectCommon(false, prog, arg...)
return exp, exp.cmd, err
}

func newExpectCommon(reap bool, prog string, arg ...string) (*Expect, error) {
name := "NewExpect"
if reap {
name = "MewExpectProc"
}

var err error
exp := new(Expect)
kill := false // On an error I want to kill the process - if it was started
defer func() {
if kill {
if err2 := exp.cmd.Process.Kill(); err2 != nil {
debugf("NewExpect cannot kill %s on error: %s", prog, err)
}
}
}()

exp.cmd = exec.Command(prog, arg...)
exp.File, err = pty.Start(exp.cmd)

if reap && exp.cmd.Process != nil {
go exp.expectReaper()
}

if err != nil {
if exp.cmd.Process != nil {
if err2 := exp.cmd.Process.Kill(); err2 != nil {
debugf("NewExpect cannot kill %s on error: %s", prog, err)
debugf("%s cannot kill %s on error: %s", name, prog, err)
}
}
return nil, err
}
go exp.expectReaper()
kill = true

// make the pty non blocking so when I read from it I dont jam up
fd := int(exp.File.Fd())
err = syscall.SetNonblock(fd, true)
if err != nil {
if err2 := exp.cmd.Process.Kill(); err2 != nil {
debugf("NewExpect cannot kill %s on error: %s", prog, err)
debugf("%s cannot kill %s on error: %s", name, prog, err)
}
return nil, err
}
Expand All @@ -188,8 +205,6 @@ func NewExpect(prog string, arg ...string) (*Expect, error) {
exp.expectReaderRunning = true
go exp.expectReader()

kill = false

return exp, err
}

Expand Down
20 changes: 20 additions & 0 deletions expect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,26 @@ func Test_NewExpect(t *testing.T) {
showWaitResult(t, exp)
}

func Test_NewExpectProc(t *testing.T) {
debugf("%s start", funcName())
defer debugf("%s end", funcName())

t.Logf("starting %s", prog)
exp, cmd, err := NewExpectProc(prog)
if err != nil {
t.Errorf("NewExpect failed %s", err)
return
}
t.Log("OK killing processes")
exp.Kill()
ps, err := cmd.Process.Wait()
if err != nil {
t.Errorf("Failed to Wait() %s", err)
return
}
t.Logf("cmd Wait() result: %s", ps)
}

func Test_Spawn(t *testing.T) {
debugf("%s start", funcName())
defer debugf("%s end", funcName())
Expand Down

0 comments on commit 39b84f8

Please sign in to comment.