Skip to main content

rvf_launch/
error.rs

1//! Error types for the RVF launcher.
2
3use std::fmt;
4use std::io;
5use std::path::PathBuf;
6
7/// All errors that the launcher can produce.
8#[derive(Debug)]
9pub enum LaunchError {
10    /// QEMU binary not found on the system.
11    QemuNotFound {
12        searched: Vec<String>,
13    },
14    /// KVM is required but not available.
15    KvmRequired,
16    /// The RVF file does not contain a KERNEL_SEG.
17    NoKernelSegment {
18        path: PathBuf,
19    },
20    /// Failed to extract kernel from the RVF file.
21    KernelExtraction(String),
22    /// Failed to create a temporary file for the extracted kernel.
23    TempFile(io::Error),
24    /// QEMU process failed to start.
25    QemuSpawn(io::Error),
26    /// QEMU process exited with a non-zero code.
27    QemuExited {
28        code: Option<i32>,
29        stderr: String,
30    },
31    /// Timeout waiting for the VM to become ready.
32    Timeout {
33        seconds: u64,
34    },
35    /// QMP protocol error.
36    Qmp(String),
37    /// I/O error communicating with QMP socket.
38    QmpIo(io::Error),
39    /// Port is already in use.
40    PortInUse {
41        port: u16,
42    },
43    /// The VM process has already exited.
44    VmNotRunning,
45    /// Generic I/O error.
46    Io(io::Error),
47}
48
49impl fmt::Display for LaunchError {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match self {
52            Self::QemuNotFound { searched } => {
53                write!(f, "QEMU not found; searched: {}", searched.join(", "))
54            }
55            Self::KvmRequired => {
56                write!(f, "KVM is required by kernel flags but /dev/kvm is not accessible")
57            }
58            Self::NoKernelSegment { path } => {
59                write!(f, "no KERNEL_SEG found in {}", path.display())
60            }
61            Self::KernelExtraction(msg) => write!(f, "kernel extraction failed: {msg}"),
62            Self::TempFile(e) => write!(f, "failed to create temp file: {e}"),
63            Self::QemuSpawn(e) => write!(f, "failed to spawn QEMU: {e}"),
64            Self::QemuExited { code, stderr } => {
65                write!(f, "QEMU exited with code {code:?}")?;
66                if !stderr.is_empty() {
67                    write!(f, ": {stderr}")?;
68                }
69                Ok(())
70            }
71            Self::Timeout { seconds } => {
72                write!(f, "VM did not become ready within {seconds}s")
73            }
74            Self::Qmp(msg) => write!(f, "QMP error: {msg}"),
75            Self::QmpIo(e) => write!(f, "QMP I/O error: {e}"),
76            Self::PortInUse { port } => write!(f, "port {port} is already in use"),
77            Self::VmNotRunning => write!(f, "VM process is not running"),
78            Self::Io(e) => write!(f, "I/O error: {e}"),
79        }
80    }
81}
82
83impl std::error::Error for LaunchError {
84    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
85        match self {
86            Self::TempFile(e) | Self::QemuSpawn(e) | Self::QmpIo(e) | Self::Io(e) => Some(e),
87            _ => None,
88        }
89    }
90}
91
92impl From<io::Error> for LaunchError {
93    fn from(e: io::Error) -> Self {
94        Self::Io(e)
95    }
96}