1use anyhow::Context;
5use diag_client::DiagClient;
6use diag_client::ExitStatus;
7use diag_client::kmsg_stream::KmsgStream;
8use futures::io::AllowStdIo;
9use std::io::Read;
10
11pub struct OpenHclDiagHandler(DiagClient);
12
13#[derive(Debug)]
15pub struct Vtl2CommandResult {
16 pub stdout: String,
18 pub stderr: String,
20 pub stdout_raw: Vec<u8>,
22 pub stderr_raw: Vec<u8>,
24 pub exit_status: ExitStatus,
26}
27
28impl OpenHclDiagHandler {
29 pub(crate) fn new(client: DiagClient) -> Self {
30 Self(client)
31 }
32
33 pub(crate) async fn wait_for_vtl2(&self) -> anyhow::Result<()> {
34 self.0.wait_for_server().await
35 }
36
37 pub(crate) async fn run_vtl2_command(
38 &self,
39 command: impl AsRef<str>,
40 args: impl IntoIterator<Item = impl AsRef<str>>,
41 ) -> anyhow::Result<Vtl2CommandResult> {
42 let client = self.diag_client().await?;
43 let mut proc = client
44 .exec(command.as_ref())
45 .args(args)
46 .stdout(true)
47 .stderr(true)
48 .raw_socket_io(true)
49 .spawn()
50 .await?;
51
52 let (mut stdout, mut stderr) = (proc.stdout.take().unwrap(), proc.stderr.take().unwrap());
53 let exit_status = proc.wait().await?;
54
55 let mut stdout_buf = Vec::new();
56 stdout
57 .read_to_end(&mut stdout_buf)
58 .context("error reading stdout socket")?;
59 let stdout_str = String::from_utf8_lossy(&stdout_buf);
60
61 let mut stderr_buf = Vec::new();
62 stderr
63 .read_to_end(&mut stderr_buf)
64 .context("error reading stderr socket")?;
65 let stderr_str = String::from_utf8_lossy(&stderr_buf);
66
67 Ok(Vtl2CommandResult {
68 stdout: stdout_str.to_string(),
69 stderr: stderr_str.to_string(),
70 stdout_raw: stdout_buf,
71 stderr_raw: stderr_buf,
72 exit_status,
73 })
74 }
75
76 pub(crate) async fn run_detached_vtl2_command(
77 &self,
78 command: impl AsRef<str>,
79 args: impl IntoIterator<Item = impl AsRef<str>>,
80 ) -> anyhow::Result<ExitStatus> {
81 let client = self.diag_client().await?;
82 let proc = client.exec(command.as_ref()).args(args).spawn().await?;
83 let exit_status = proc.wait().await?;
84 Ok(exit_status)
85 }
86
87 pub async fn core_dump(&self, name: &str, path: &std::path::Path) -> anyhow::Result<()> {
88 let client = self.diag_client().await?;
89 let pid = client.get_pid(name).await?;
90 client
91 .core_dump(
92 pid,
93 AllowStdIo::new(fs_err::File::create(path)?),
94 AllowStdIo::new(std::io::stderr()),
95 true,
96 )
97 .await
98 }
99
100 pub async fn crash(&self, name: &str) -> anyhow::Result<()> {
101 let client = self.diag_client().await?;
102 let pid = client.get_pid(name).await?;
103 client.crash(pid).await
104 }
105
106 pub async fn inspect(
107 &self,
108 path: impl Into<String>,
109 depth: Option<usize>,
110 timeout: Option<std::time::Duration>,
111 ) -> anyhow::Result<inspect::Node> {
112 self.diag_client()
113 .await?
114 .inspect(path, depth, timeout)
115 .await
116 }
117
118 pub async fn kmsg(&self) -> anyhow::Result<KmsgStream> {
119 self.diag_client().await?.kmsg(false).await
120 }
121
122 async fn diag_client(&self) -> anyhow::Result<&DiagClient> {
123 self.wait_for_vtl2().await?;
124 Ok(&self.0)
125 }
126}