use std::sync::Arc;
use std::time::Duration;
use arcbox_vm_driver::net::NetworkIdentity;
use arcbox_vm_driver::{PreparedVm, VmHandle};
use async_trait::async_trait;
use tokio::sync::mpsc;
use crate::boot_proto::NetReconfigCommand;
use crate::error::Result;
use crate::file_proto::{FileStatDto, FsEventDto};
pub mod vm_proto;
pub use vm_proto::{VmProtoAgent, VmProtoAgentFactory};
pub use arcbox_vm_proto::exec::StartCommand;
pub type ExecInput = mpsc::Sender<ExecInputMsg>;
#[async_trait]
pub trait GuestAgentFactory: Send + Sync {
fn readiness(&self) -> Readiness;
async fn arm_readiness(&self, prepared: &dyn PreparedVm) -> Result<Box<dyn ReadyGate>>;
fn connect(
&self,
handle: Arc<dyn VmHandle>,
net: Option<&NetworkIdentity>,
) -> Result<Arc<dyn GuestAgent>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Readiness {
DialOut { port: u32 },
Poll { port: u32 },
Probe,
}
#[async_trait]
pub trait ReadyGate: Send {
async fn wait(&mut self, handle: &Arc<dyn VmHandle>, agent: &Arc<dyn GuestAgent>)
-> Result<()>;
}
pub struct ProbeGate;
#[async_trait]
impl ReadyGate for ProbeGate {
async fn wait(
&mut self,
_handle: &Arc<dyn VmHandle>,
agent: &Arc<dyn GuestAgent>,
) -> Result<()> {
agent.sync_clock().await.map(drop)
}
}
#[async_trait]
pub trait GuestAgent: Send + Sync {
async fn run(&self, start: StartCommand) -> Result<mpsc::Receiver<Result<OutputChunk>>>;
async fn exec(
&self,
start: StartCommand,
) -> Result<(ExecInput, mpsc::Receiver<Result<OutputChunk>>)>;
async fn sync_clock(&self) -> Result<ClockSync>;
async fn reconfigure_network(&self, cmd: &NetReconfigCommand) -> Result<()>;
async fn wait_for_port(&self, port: u16, timeout: Duration) -> Result<PortWait>;
fn files(&self) -> &dyn GuestFiles;
}
#[async_trait]
pub trait GuestFiles: Send + Sync {
async fn read(&self, path: &str) -> Result<Vec<u8>>;
async fn write(&self, path: &str, mode: u32, data: &[u8]) -> Result<()>;
async fn stat(&self, path: &str) -> Result<FileStatDto>;
async fn list(&self, path: &str) -> Result<Vec<FileStatDto>>;
async fn make_dir(&self, path: &str, mode: u32) -> Result<()>;
async fn remove(&self, path: &str, recursive: bool) -> Result<()>;
async fn rename(&self, from: &str, to: &str) -> Result<()>;
async fn watch(&self, path: &str, recursive: bool) -> Result<DirWatch>;
}
pub struct DirWatch(Box<dyn FsEvents>);
impl DirWatch {
#[must_use]
pub fn new(events: Box<dyn FsEvents>) -> Self {
Self(events)
}
pub async fn next_event(&mut self) -> Result<Option<FsEventDto>> {
self.0.next_event().await
}
}
impl std::fmt::Debug for DirWatch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DirWatch").finish_non_exhaustive()
}
}
#[async_trait]
pub trait FsEvents: Send {
async fn next_event(&mut self) -> Result<Option<FsEventDto>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExitStatus {
Code(i32),
Signaled(i32),
}
impl ExitStatus {
#[must_use]
pub const fn conventional_code(self) -> i32 {
match self {
Self::Code(code) => code,
Self::Signaled(signal) => 128 + signal,
}
}
}
#[derive(Debug, Clone)]
pub enum OutputChunk {
Stdout(Vec<u8>),
Stderr(Vec<u8>),
Exit(ExitStatus),
}
#[derive(Debug)]
pub enum ExecInputMsg {
Stdin(Vec<u8>),
Resize { width: u16, height: u16 },
Signal(i32),
Eof,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortWait {
Listening,
Deadline,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClockSync {
Synced,
AgentError(i32),
}