Skip to content

rexpect interact(), giving control back to the user #30

Description

@chama-chomo

Is there any way to return control of the terminal back to the user ? e.g. after connecting to a remote server. Thanks.
In python pexpect I use interact() method to achieve this.

Activity

  1. chama-chomo commented on Nov 1, 2020

    @chama-chomo
    Author

    as a Rust newbie I was able to create kind of a POC for my ssh repl, my only problem is it doesn't feel very much like a standard ssh session with command/path expansion and so. There is definitely a better way. Nevertheless, I'm going to provide my piece of code for someone like me, as inspiration (this is my specific case ofc, not too much generic)..

    ...
        fn connect_ssh(&self, dest: Option<&String>) -> Result<()> {
            ConnectAction::do_ssh_repl(&dest.unwrap())
                .unwrap_or_else(|e| panic!("ssh session failed with {}", e));
            Ok(())
        }
    
        fn do_ssh_repl(host: &String) -> Result<()> {
            let mut ssh = ConnectAction::create_ssh_session(&host)?;
            ssh.exp_regex("\r\n.*root.*:~# ")?;
            ConnectAction::interact(&mut ssh)?;
            ssh.exp_eof()?;
            Ok(())
        }
    
        fn create_ssh_session(host: &String) -> Result<PtyReplSession> {
            println!("Connecting to host {}", host);
            let custom_prompt = format!("root@{}'s password: ", host);
            let custom_command = format!("ssh root@{}", host);
            let mut ssh = PtyReplSession {
                echo_on: false,
                prompt: custom_prompt,
                pty_session: spawn(&*custom_command.into_boxed_str(), Some(5000))?,
                quit_command: Some("Q".to_string()),
            };
            ssh.wait_for_prompt()?;
            ssh.send_line("root")?;
            Ok(ssh)
        }
    
        fn interact(session: &mut PtyReplSession) -> Result<()> {
            while let Some(WaitStatus::StillAlive) = session.process.status() {
                let mut user_input = String::new();
                print!("ssh session :> ");
                io::stdout().flush().expect("couldn't flush buffer");
                io::stdin().read_line(&mut user_input).expect("error: unable to read user input");
                session.writer.write(&user_input.into_bytes()).expect("could not write");
    
                let re = Regex::new(r".*root@.*:~# ").unwrap();
                let (before, _) = session.reader.read_until(&rexpect::ReadUntil::Regex(re)).unwrap();
    
                println!("{}", before);
            };
            Ok(())
        }
    
  2. BarretRen commented on Nov 21, 2022

    @BarretRen

    @chama-chomo, your workaround looks good, but it's still not perfect.
    For example, if I start gdb task with rexpect, i want a real gdb tty after calling interact. But in this way, it can't offer me that.
    This issue is opened two years before, but no updates for it. I will use pexpect for my tools.
    Life is short, I use python.^v^

  3. wolfv commented on Aug 30, 2023

    @wolfv

    We've implemented an interact functionality in the pixi package manager (to spawn an interactive shell where we can source some scripts after the shell has started, and then give control to the user).

    You can have a look at the code here: https://github.com/prefix-dev/pixi/pull/316/files

    There were some changes:

    • set terminal to raw mode
    • enable ECHO for the terminal (to see what user types) - this is always deactivated in rexpect
    • be able to set the window size when the SIGWINCH signal happens
    • use select on the two filestreams (stdin of the parent and stdout of the child) to forward the input / output

    We've gone through a few iterations with this code and are reasonably happy with it now. I am not sure what the appetite is for these changes in rexpect. They are a bit deeper (especially with the ECHO part), but we could potentially make a PR to contribute the feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions