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());
}
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With