Skip to content
This repository has been archived by the owner on Feb 7, 2023. It is now read-only.

Is there a Interact() interface #5

Open
gftea opened this issue Sep 2, 2017 · 15 comments
Open

Is there a Interact() interface #5

gftea opened this issue Sep 2, 2017 · 15 comments

Comments

@gftea
Copy link

gftea commented Sep 2, 2017

Hi,

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

@skalle
Copy link
Contributor

skalle commented Sep 4, 2017

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?

@gftea
Copy link
Author

gftea commented Sep 4, 2017

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.

@skalle
Copy link
Contributor

skalle commented Sep 8, 2017

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
Copy link
Contributor

skalle commented Sep 8, 2017

Or a pull request .. :)

@skalle
Copy link
Contributor

skalle commented Sep 21, 2017

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.

@gftea
Copy link
Author

gftea commented Sep 21, 2017

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

@skalle
Copy link
Contributor

skalle commented Sep 22, 2017

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.

@gftea
Copy link
Author

gftea commented Sep 23, 2017

sounds great

@skalle
Copy link
Contributor

skalle commented Oct 30, 2017

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
Copy link
Contributor

skalle commented Nov 14, 2017

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.

@vegeta1509
Copy link

vegeta1509 commented Sep 7, 2020

Hey,

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

@vegeta1509
Copy link

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

@skalle
Copy link
Contributor

skalle commented Sep 7, 2020

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?

@vegeta1509
Copy link

vegeta1509 commented Sep 7, 2020

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.

@cedarwu
Copy link

cedarwu commented Mar 28, 2021

Anyone got a solution for interact in goexpect ?

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

No branches or pull requests

4 participants