1use super::*;
2use std::process::{Child, Command};
3use std::time::Duration;
4use wait_timeout::ChildExt;
5
6#[derive(Debug, Clone)]
8pub struct QemuConfig {
9 pub qemu_path: String,
10 pub bios_path: String,
11 pub drives: Vec<QemuDriveConfig>,
12 pub additional_args: Vec<String>,
13}
14
15impl Default for QemuConfig {
16 fn default() -> Self {
17 Self {
18 qemu_path: "qemu-system-x86_64".to_string(),
19 bios_path: "OVMF.fd".to_string(),
20 drives: Vec::new(),
21 additional_args: vec!["-net".to_string(), "none".to_string()],
22 }
23 }
24}
25
26impl QemuConfig {
27 pub fn run(&self) -> Result<QemuProcess> {
29 let mut args = vec!["-bios".to_string(), self.bios_path.clone()];
30 for (index, drive) in self.drives.iter().enumerate() {
31 args.push("-drive".to_string());
32 args.push(format!(
33 "file={},index={},media={},format={}",
34 drive.file, index, drive.media, drive.format
35 ));
36 }
37 args.extend(self.additional_args.iter().cloned());
38
39 let child = Command::new(&self.qemu_path).args(args).spawn()?;
40 Ok(QemuProcess { child })
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct QemuDriveConfig {
47 pub file: String,
48 pub media: String,
49 pub format: String,
50}
51
52impl QemuDriveConfig {
53 pub fn new(file: &str, media: &str, format: &str) -> Self {
54 Self {
55 file: file.to_string(),
56 media: media.to_string(),
57 format: format.to_string(),
58 }
59 }
60}
61
62pub struct QemuProcess {
63 child: Child,
64}
65
66impl QemuProcess {
67 pub fn wait(&mut self, duration: Duration) -> Option<i32> {
71 self.child
72 .wait_timeout(duration)
73 .expect("Failed to wait on child process")
74 .map(|exit_status| exit_status.code().unwrap_or(0))
75 }
76
77 pub fn kill(&mut self) -> std::io::Result<()> {
79 self.child.kill()
80 }
81}