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

Is there a Interact() interface ? #24

Open
cedarwu opened this issue Mar 28, 2021 · 2 comments
Open

Is there a Interact() interface ? #24

cedarwu opened this issue Mar 28, 2021 · 2 comments

Comments

@cedarwu
Copy link

cedarwu commented Mar 28, 2021

How to gives control of the child process to the interactive user (the human at the keyboard) like the interact method in expect/pexpect?
https://wiki.tcl-lang.org/page/interact

@hinshun
Copy link
Contributor

hinshun commented Mar 28, 2021

Send os.Stdin into the expect.Console. This library is a bit lower level so we expect users to decide how to manage the IO and spawn the child processes.

@cedarwu
Copy link
Author

cedarwu commented Mar 29, 2021

Can't make the child shell work as normal, could you please give some advice?
The shell works, but completion and ctrl-c does not work.

Code as following:
First, make expect to login into another machine, then forward all signals to subprocess and wait for finish.

package main

import (
	"log"
	"os"
	"os/exec"
	"os/signal"
	"syscall"

	expect "github.com/Netflix/go-expect"
)

func main() {
	c, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithStdin(os.Stdin))
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	cmd := exec.Command("ssh", "root@ip", "-i", "~/.ssh/id_rsa")
	cmd.Stdin = c.Tty()
	cmd.Stdout = c.Tty()
	cmd.Stderr = c.Tty()

	err = cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	_, err = c.ExpectString("[root@")
	if err != nil {
		log.Fatalln("expect err: [root@")
		return
	}

	// wait for the command to finish
	waitCh := make(chan error, 1)
	go func() {
		c.ExpectEOF()
		waitCh <- cmd.Wait()
		close(waitCh)
	}()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan)

	// You need a for loop to handle multiple signals
	for {
		select {
		case sig := <-sigChan:
			if err := cmd.Process.Signal(sig); err != nil {
				log.Println("error sending signal", sig, err)
			}
		case err := <-waitCh:
			log.Println("subprocess exited")
			var waitStatus syscall.WaitStatus
			if exitError, ok := err.(*exec.ExitError); ok {
				waitStatus = exitError.Sys().(syscall.WaitStatus)
				os.Exit(waitStatus.ExitStatus())
			}
			if err != nil {
				log.Fatal(err)
			}
			break
		}
	}
}

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

2 participants