2019-05-21 18:56:39 +03:00
|
|
|
//! This is a conceptually simple example that spawns the `whoami` program
|
|
|
|
//! to print your username. It is made more complex because there are multiple
|
|
|
|
//! pipes involved and it is easy to get blocked/deadlocked if care and attention
|
|
|
|
//! is not paid to those pipes!
|
2020-01-20 04:32:33 +03:00
|
|
|
use portable_pty::{CommandBuilder, NativePtySystem, PtySize, PtySystem};
|
2022-08-12 17:38:18 +03:00
|
|
|
use std::sync::mpsc::channel;
|
2019-05-20 17:20:47 +03:00
|
|
|
|
|
|
|
fn main() {
|
2020-01-20 04:32:33 +03:00
|
|
|
let pty_system = NativePtySystem::default();
|
2019-05-20 17:20:47 +03:00
|
|
|
|
2019-06-09 17:33:00 +03:00
|
|
|
let pair = pty_system
|
2019-05-20 17:20:47 +03:00
|
|
|
.openpty(PtySize {
|
|
|
|
rows: 24,
|
|
|
|
cols: 80,
|
|
|
|
pixel_width: 0,
|
|
|
|
pixel_height: 0,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let cmd = CommandBuilder::new("whoami");
|
2019-06-09 17:33:00 +03:00
|
|
|
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
2022-08-12 17:38:18 +03:00
|
|
|
|
2019-05-21 18:56:39 +03:00
|
|
|
// Release any handles owned by the slave: we don't need it now
|
|
|
|
// that we've spawned the child.
|
2019-06-09 17:33:00 +03:00
|
|
|
drop(pair.slave);
|
2019-05-20 17:20:47 +03:00
|
|
|
|
2022-08-12 18:32:50 +03:00
|
|
|
// Read the output in another thread.
|
|
|
|
// This is important because it is easy to encounter a situation
|
|
|
|
// where read/write buffers fill and block either your process
|
|
|
|
// or the spawned process.
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
let mut reader = pair.master.try_clone_reader().unwrap();
|
|
|
|
std::thread::spawn(move || {
|
|
|
|
// Consume the output from the child
|
|
|
|
let mut s = String::new();
|
|
|
|
reader.read_to_string(&mut s).unwrap();
|
|
|
|
tx.send(s).unwrap();
|
|
|
|
});
|
|
|
|
|
2022-08-12 17:38:18 +03:00
|
|
|
{
|
|
|
|
// Obtain the writer.
|
|
|
|
// When the writer is dropped, EOF will be sent to
|
|
|
|
// the program that was spawned.
|
|
|
|
// It is important to take the writer even if you don't
|
|
|
|
// send anything to its stdin so that EOF can be
|
|
|
|
// generated, otherwise you risk deadlocking yourself.
|
|
|
|
let mut writer = pair.master.take_writer().unwrap();
|
|
|
|
|
2022-08-12 18:32:50 +03:00
|
|
|
if cfg!(target_os = "macos") {
|
|
|
|
// macOS quirk: the child and reader must be started and
|
|
|
|
// allowed a brief grace period to run before we allow
|
|
|
|
// the writer to drop. Otherwise, the data we send to
|
|
|
|
// the kernel to trigger EOF is interleaved with the
|
|
|
|
// data read by the reader! WTF!?
|
|
|
|
// This appears to be a race condition for very short
|
|
|
|
// lived processes on macOS.
|
|
|
|
// I'd love to find a more deterministic solution to
|
|
|
|
// this than sleeping.
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(20));
|
|
|
|
}
|
|
|
|
|
2022-08-12 17:38:18 +03:00
|
|
|
// This example doesn't need to write anything, but if you
|
|
|
|
// want to send data to the child, you'd set `to_write` to
|
|
|
|
// that data and do it like this:
|
|
|
|
let to_write = "";
|
|
|
|
if !to_write.is_empty() {
|
|
|
|
// To avoid deadlock, wrt. reading and waiting, we send
|
|
|
|
// data to the stdin of the child in a different thread.
|
|
|
|
std::thread::spawn(move || {
|
|
|
|
writer.write_all(to_write.as_bytes()).unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the child to complete
|
|
|
|
println!("child status: {:?}", child.wait().unwrap());
|
|
|
|
|
|
|
|
// Take care to drop the master after our processes are
|
|
|
|
// done, as some platforms get unhappy if it is dropped
|
|
|
|
// sooner than that.
|
2019-06-09 17:33:00 +03:00
|
|
|
drop(pair.master);
|
2019-05-21 18:56:39 +03:00
|
|
|
|
2022-08-12 17:38:18 +03:00
|
|
|
// Now wait for the output to be read by our reader thread
|
|
|
|
let output = rx.recv().unwrap();
|
2019-05-21 18:56:39 +03:00
|
|
|
|
|
|
|
// We print with escapes escaped because the windows conpty
|
|
|
|
// implementation synthesizes title change escape sequences
|
|
|
|
// in the output stream and it can be confusing to see those
|
|
|
|
// printed out raw in another terminal.
|
|
|
|
print!("output: ");
|
2022-08-12 17:38:18 +03:00
|
|
|
for c in output.escape_debug() {
|
2019-05-21 18:56:39 +03:00
|
|
|
print!("{}", c);
|
|
|
|
}
|
2019-05-20 17:20:47 +03:00
|
|
|
}
|