1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
use std::{
io::Write,
sync::{Arc, Mutex},
};
use bytes::Bytes;
use eyre::{eyre, Result};
use portable_pty::{CommandBuilder, MasterPty, PtySize};
pub struct Pty {
tx: tokio::sync::mpsc::Sender<Bytes>,
pub master: Arc<Mutex<Box<dyn MasterPty + Send>>>,
pub reader: Arc<Mutex<Box<dyn std::io::Read + Send>>>,
}
impl Pty {
pub async fn open<'a>(rows: u16, cols: u16) -> Result<Self> {
let sys = portable_pty::native_pty_system();
let pair = sys
.openpty(PtySize {
rows,
cols,
pixel_width: 0,
pixel_height: 0,
})
.map_err(|e| eyre!("Failed to open pty: {}", e))?;
let cmd = CommandBuilder::new_default_prog();
tokio::task::spawn_blocking(move || {
let mut child = pair.slave.spawn_command(cmd).unwrap();
// Wait for the child to exit
let _ = child.wait().unwrap();
// Ensure slave is dropped
// This closes file handles, we can deadlock if this is not done correctly.
drop(pair.slave);
});
// Handle input -> write to master writer
let (master_tx, mut master_rx) = tokio::sync::mpsc::channel::<Bytes>(32);
let mut writer = pair.master.take_writer().unwrap();
let reader = pair
.master
.try_clone_reader()
.map_err(|e| e.to_string())
.expect("Failed to clone reader");
tokio::spawn(async move {
while let Some(bytes) = master_rx.recv().await {
writer.write_all(&bytes).unwrap();
writer.flush().unwrap();
}
// When the channel has been closed, we won't be getting any more input. Close the
// writer and the master.
// This will also close the writer, which sends EOF to the underlying shell. Ensuring
// that is also closed.
drop(writer);
});
Ok(Pty {
tx: master_tx,
master: Arc::new(Mutex::new(pair.master)),
reader: Arc::new(Mutex::new(reader)),
})
}
pub async fn resize(&self, rows: u16, cols: u16) -> Result<()> {
let master = self
.master
.lock()
.map_err(|e| eyre!("Failed to lock pty master: {e}"))?;
master
.resize(PtySize {
rows,
cols,
pixel_width: 0,
pixel_height: 0,
})
.map_err(|e| eyre!("Failed to resize terminal: {e}"))?;
Ok(())
}
pub async fn send_bytes(&self, bytes: Bytes) -> Result<()> {
self.tx
.send(bytes)
.await
.map_err(|e| eyre!("Failed to write to master tx: {}", e))
}
pub async fn send_string(&self, cmd: &str) -> Result<()> {
let bytes: Vec<u8> = cmd.bytes().collect();
let bytes = Bytes::from(bytes);
self.send_bytes(bytes).await
}
pub async fn send_single_string(&self, cmd: &str) -> Result<()> {
let mut bytes: Vec<u8> = cmd.bytes().collect();
bytes.push(0x04);
let bytes = Bytes::from(bytes);
self.send_bytes(bytes).await
}
}
|