1extern crate openvmm_hypervisors as _;
13
14pub mod disk_image;
15pub mod initrd;
16mod linux_direct_serial_agent;
17pub mod mshv_boot;
18pub mod mshv_image;
19#[expect(missing_docs)]
22pub mod openhcl_diag;
23pub mod requirements;
24mod test;
25mod tracing;
26mod vm;
27mod worker;
28
29pub use petri_artifacts_core::ArtifactHandle;
30pub use petri_artifacts_core::ArtifactResolver;
31pub use petri_artifacts_core::ArtifactSource;
32pub use petri_artifacts_core::AsArtifactHandle;
33pub use petri_artifacts_core::ErasedArtifactHandle;
34pub use petri_artifacts_core::RemoteAccess;
35pub use petri_artifacts_core::ResolveTestArtifact;
36pub use petri_artifacts_core::ResolvedArtifact;
37pub use petri_artifacts_core::ResolvedArtifactSource;
38pub use petri_artifacts_core::ResolvedOptionalArtifact;
39pub use petri_artifacts_core::TestArtifactRequirements;
40pub use petri_artifacts_core::TestArtifacts;
41pub use pipette_client as pipette;
42pub use test::PetriTestParams;
43pub use test::RunTest;
44pub use test::SimpleTest;
45pub use test::TestCase;
46pub use test::test_macro_support;
47pub use test::test_main;
48pub use tracing::*;
49pub use vm::*;
50
51use jiff::Timestamp;
52use pal_async::process::PolledChild;
53use std::ops::Deref;
54use std::ops::DerefMut;
55use std::process::Command;
56use std::process::Stdio;
57use thiserror::Error;
58
59pub const SIZE_1_KB: u64 = 1024;
61pub const SIZE_1_MB: u64 = 1024 * SIZE_1_KB;
63pub const SIZE_1_GB: u64 = 1024 * SIZE_1_MB;
65
66#[expect(missing_docs)] pub enum ShutdownKind {
69 Shutdown,
70 Reboot,
71 Hibernate,
72}
73
74#[derive(Error, Debug)]
76pub enum CommandError {
77 #[error("failed to launch command")]
79 Launch(#[from] std::io::Error),
80 #[error("command exited with non-zero status ({0}): {1}")]
82 Command(std::process::ExitStatus, String),
83}
84
85pub async fn run_host_cmd(mut cmd: Command) -> Result<String, CommandError> {
87 cmd.stderr(Stdio::piped()).stdin(Stdio::null());
88
89 let cmd_debug = format!("{cmd:?}");
90 ::tracing::debug!(cmd = cmd_debug, "executing command");
91
92 let start = Timestamp::now();
93 let output = blocking::unblock(move || cmd.output()).await?;
94 let time_elapsed = Timestamp::now() - start;
95
96 let stdout_str = String::from_utf8_lossy(&output.stdout).to_string();
97 let stderr_str = String::from_utf8_lossy(&output.stderr).to_string();
98 ::tracing::debug!(
99 cmd = cmd_debug,
100 stdout_str,
101 stderr_str,
102 "command exited in {:.3}s with status {}",
103 time_elapsed.total(jiff::Unit::Second).unwrap(),
104 output.status
105 );
106
107 if !output.status.success() {
108 return Err(CommandError::Command(output.status, stderr_str));
109 }
110
111 Ok(stdout_str.trim().to_owned())
112}
113
114pub struct TestChild(PolledChild<std::process::Child>);
120
121impl TestChild {
122 pub fn new(child: PolledChild<std::process::Child>) -> Self {
124 Self(child)
125 }
126}
127
128impl Deref for TestChild {
129 type Target = PolledChild<std::process::Child>;
130
131 fn deref(&self) -> &Self::Target {
132 &self.0
133 }
134}
135
136impl DerefMut for TestChild {
137 fn deref_mut(&mut self) -> &mut Self::Target {
138 &mut self.0
139 }
140}
141
142impl Drop for TestChild {
143 fn drop(&mut self) {
144 let child = self.0.get_mut();
145 if let Ok(Some(_)) = child.try_wait() {
148 return;
149 }
150 ::tracing::warn!(
151 "killing test child process which was still running at the end of the test"
152 );
153 if let Err(err) = child.kill() {
154 ::tracing::warn!(
155 error = &err as &dyn std::error::Error,
156 "failed to kill test child process"
157 );
158 return;
159 }
160 let _ = child.wait();
163 }
164}