petri/
linux_direct_serial_agent.rs1use futures::AsyncReadExt;
5use futures::AsyncWriteExt;
6use pal_async::socket::ReadHalf;
7use pal_async::socket::WriteHalf;
8use unix_socket::UnixStream;
9
10const BUSYBOX_INIT: &str = "/bin/busybox --install /bin && mount none /dev -t devtmpfs && mount none /proc -t proc && mount none /sys -t sysfs";
11
12pub(crate) struct LinuxDirectSerialAgent {
13 write: WriteHalf<UnixStream>,
15 read: ReadHalf<UnixStream>,
17 init: bool,
19}
20
21impl LinuxDirectSerialAgent {
22 pub(crate) fn new(
23 serial1_read: ReadHalf<UnixStream>,
24 serial0_write: WriteHalf<UnixStream>,
25 ) -> Self {
26 Self {
27 read: serial1_read,
28 write: serial0_write,
29 init: false,
30 }
31 }
32}
33
34impl LinuxDirectSerialAgent {
35 pub(crate) fn reset(&mut self) {
37 self.init = false;
38 }
39
40 pub(crate) async fn run_command(&mut self, command: &str) -> anyhow::Result<String> {
41 self.init_busybox_if_necessary().await?;
42 let bytes = self.run_command_core(command).await?;
43 Ok(String::from_utf8_lossy(&bytes).into_owned())
44 }
45
46 async fn run_command_core(&mut self, command: &str) -> anyhow::Result<Vec<u8>> {
47 const COMMAND_END_SIGNAL: &str = "== Petri Command Complete ==";
52 let command = format!("({command}) > /dev/ttyS1\necho {COMMAND_END_SIGNAL} > /dev/ttyS1\n");
53
54 const COMMAND_END_SIGNAL_READ: &str = "== Petri Command Complete ==\r\n";
56
57 self.write.write_all(command.as_bytes()).await?;
58
59 let mut output = Vec::new();
60 let mut buf = [0u8; 1024];
61 loop {
62 let n = self.read.read(&mut buf).await?;
63 tracing::debug!(buf = ?&buf[..n], "read serial bytes from guest");
64 output.extend_from_slice(&buf[..n]);
65 if output.ends_with(COMMAND_END_SIGNAL_READ.as_bytes()) {
66 output.truncate(output.len() - COMMAND_END_SIGNAL_READ.len());
67 break;
68 }
69 }
70
71 Ok(output)
72 }
73
74 async fn init_busybox_if_necessary(&mut self) -> anyhow::Result<()> {
75 if !self.init {
76 self.run_command_core(BUSYBOX_INIT).await?;
77 self.init = true;
78 };
79 Ok(())
80 }
81}