goexpect icon indicating copy to clipboard operation
goexpect copied to clipboard

Is there a Interact() interface

Open gftea opened this issue 8 years ago • 15 comments

Hi,

How to gives control of the child process to the interactive user (the human at the keyboard) like the interact() method in pexpect?

gftea avatar Sep 02 '17 20:09 gftea

Hey Kenny.

No there's no interact() interface in this expect , purely b/c we haven't had the usecase for it.

You'd like for a human to interact with the process until pressing some keycombo to give the control back to the expect batch?

skalle avatar Sep 04 '17 07:09 skalle

Hi,

Use case as below, use expect to login by ssh and run batch commands, then give ssh control back to user so that user can start to work on ssh session.

gftea avatar Sep 04 '17 07:09 gftea

Hey Kenny ..

Late response , pretty hectic atm. Happy to have Interact added to this package.

I'll sort out a design next week , feel free to add anything here that you'd like for the interact interface.

skalle avatar Sep 08 '17 02:09 skalle

Or a pull request .. :)

skalle avatar Sep 08 '17 02:09 skalle

Ok have put some thought/code into this ..

My thinking is to expose Reader and Writer interfaces from expecter ..

With this you can just read/write the Expecter..

So doing an interact thing to your current terminal should be as simple as.

e := Spawn(bla) .... e.Send("User") e.Expect(bla) e.Send("pass123) e.Expect("Logged in") ... // Would need some more logic to check for some key combo to end interact. // Maybe some term settings and such. go io.Copy(e,os.Stdin) go io.Copy(os.Stdout,e) ... e.Send("logout\n")

Still working on implementing this in a proper way , some changes to the Expect logic needed here.

skalle avatar Sep 21 '17 08:09 skalle

Hi Skalle,

If providing API as pexpect, the use case will be as below e := Spawn(bla) .... e.Send("User") e.Expect(bla) e.Send("pass123) e.Expect("Logged in") e.Interact() // this gives control of the child process to the interactive user, so that the process receive the user keyboard input directly, and maybe suppot customization of readline ... // user can use Ctrl+D or Ctrl+C to close the session

gftea avatar Sep 21 '17 18:09 gftea

Still some stuff to work out , code so far is in the intent branch.

With this change the Expecter implements the Reader/Writer interface for you to interact with.

I think this'd make it more powerful and versatile than just adding the Interact function , this way you can tie the I/O to whatever you want eg. a PTY from term or some remote network pipe. Gives you full control of how to end the interaction too , write a phrase or type some mysterious character.

We can still add the Interact function on top of the Reader/Writer as a convinence thing just using the os.Stdin/os.Stdout for I/O.

Status is still work-in-progress. Off for some rest and weekend now , will mess around more with this next week.

skalle avatar Sep 22 '17 07:09 skalle

sounds great

gftea avatar Sep 23 '17 17:09 gftea

Sorry for the long delay here .. Perf time at work so very limited time for this.

Haven't forgotten about this, just need to find some time.

skalle avatar Oct 30 '17 04:10 skalle

Incase someone want to have a stab at this one, feel free..

Fine to use some of the code I wrote to handle this or start from scratch.

skalle avatar Nov 14 '17 05:11 skalle

Hey,

Have you figured it out how to add an interactive session?

vegeta1509 avatar Sep 07 '20 10:09 vegeta1509

I'm also working on a similar project and I also need an interactive session after logging in through ssh using expect

vegeta1509 avatar Sep 07 '20 10:09 vegeta1509

Hey vegeta1509 .

Sorry but this has been put on the backburner .. I did write an interact version earlier when the bug was filed but it didnt' fit very well into the library. Added complexity and was more of a hack.

Could you describe what you need here a bit further?

skalle avatar Sep 07 '20 11:09 skalle

When we run command ssh root@xyz, then it login to a server and returns a PTY with which user can interact and can run whatever command they want. I want something similar I want to run some command using expect just after login to the server and after that I want to attach a PTY so that user can interact. In SpawnSSHPTY method the code sshclient creates a session and then closes it in the function itself

// SpawnSSHPTY starts an interactive SSH session and ties it to a local PTY, optionally requests a remote PTY. func SpawnSSHPTY(sshClient *ssh.Client, timeout time.Duration, term term.Termios, opts ...Option) (*GExpect, <-chan error, error) { if sshClient == nil { return nil, nil, errors.New("*ssh.Client is nil") } if timeout < 1 { timeout = DefaultTimeout } // Setup interactive session session, err := sshClient.NewSession() if err != nil { return nil, nil, err } e := &GExpect{ rcv: make(chan struct{}), snd: make(chan string), chk: func(e *GExpect) bool { if e.ssh == nil { return false } _, err := e.ssh.SendRequest("dummy", false, nil) return err == nil }, cls: func(e *GExpect) error { if e.ssh != nil { return e.ssh.Close() } return nil }, ssh: session, timeout: timeout, chkDuration: checkDuration, } for _, o := range opts { o(e) } if term.Wz.WsCol == 0 { term.Wz.WsCol = sshTermWidth } if term.Wz.WsRow == 0 { term.Wz.WsRow = sshTermHeight } if err := session.RequestPty(sshTerm, int(term.Wz.WsRow), int(term.Wz.WsCol), term.ToSSH()); err != nil { return nil, nil, err } inPipe, err := session.StdinPipe() if err != nil { return nil, nil, err } outPipe, err := session.StdoutPipe() if err != nil { return nil, nil, err } errPipe, err := session.StderrPipe() if err != nil { return nil, nil, err } if err := session.Shell(); err != nil { return nil, nil, err } // Shell started. errCh := make(chan error, 1) go e.waitForSession(errCh, session.Wait, inPipe, outPipe, errPipe) return e, errCh, nil }

I've tried creating the session in main function and then passed session instead of client in the function. And after running my commands using expect I tried changing the I/O pipes so that it can work as interactive PTY.

go io.Copy(os.Stdout, outPipe) go io.Copy(inPipe, os.Stdin) go io.Copy(errPipe, os.Stderr)

But it's not working properly, the server gives output sometimes, sometimes it doesn't. Many times it doesn't take input properly. So I think the I/O pipes are not transmitting data properly. If you could help me in figuring out correct way to attach these I/O pipes then that would be great help.

vegeta1509 avatar Sep 07 '20 12:09 vegeta1509

Anyone got a solution for interact in goexpect ?

cedarwu avatar Mar 28 '21 15:03 cedarwu