Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you write to a pty master Rust

Tags:

unix

rust

tty

pty

libc

I have created a simple pty setup, however I am unsure on how to actually write to the master or slave sides once it is created. I am also unsure if my setup is correct, because upon inspection, the Stdin, Stdout, and Stderr of the child process for the pty are all None instead of being set to the slave file descriptor. Would anyone be able to clarify if this is correct and if not do you have any suggestion on how to fix it?

use libc::{self};
use nix::pty::openpty;
use std::os::unix::io::FromRawFd;
use std::process::{Child, Command, Stdio};

#[derive(Debug)]
pub struct Pty {
    process: Child,
    fd: i32,
}

fn create_pty(process: &str) -> Pty {
    let ends = openpty(None, None).expect("openpty failed");
    let master = ends.master;
    let slave = ends.slave;

    let mut builder = Command::new(process);
    builder.stdin(unsafe { Stdio::from_raw_fd(slave) });
    builder.stdout(unsafe { Stdio::from_raw_fd(slave) });
    builder.stderr(unsafe { Stdio::from_raw_fd(slave) });

    match builder.spawn() {
        Ok(process) => {
            let pty = Pty {
                process,
                fd: master,
            };

            pty
        }
        Err(e) => {
            panic!("Failed to create pty: {}", e);
        }
    }
}

fn main() {
    let shell = "/bin/bash";

    let pty = create_pty(shell);
    println!("{:?}", pty);

    println!("{}", pty.process.id());
}
like image 956
Lee Avatar asked Feb 23 '20 22:02

Lee


1 Answers

This is expected. std::process::Child::stdin and friends are not set for raw file handles (because Rust doesn't know what they are, the builder doesn't have the master end of your pty).

You can construct your rusty file handle for the master yourself:

fn main() {
    let shell = "/bin/bash";

    let pty = create_pty(shell);
    println!("{:?}", pty);

    let mut output = unsafe { File::from_raw_fd(pty.fd) };
    write!(output, "touch /tmp/itworks\n");
    output.flush();

    std::thread::sleep_ms(1000);

    println!("{}", pty.process.id());
}

You'll see that this does indeed create a file "/tmp/itworks".

(Permalink to the playground)

like image 154
mcarton Avatar answered Oct 24 '22 02:10

mcarton