use std::path::Path;
use microsandbox_agent_client::{AgentClient, TypedMessage};
use microsandbox_protocol::{
exec::{ExecExited, ExecFailed, ExecRequest},
message::MessageType,
};
use crate::vm::StartupCommand;
use crate::{RuntimeError, RuntimeResult};
pub(crate) enum StartupCommandExit {
Exited(i32),
Failed(ExecFailed),
}
pub(crate) async fn run_startup_command(
agent_sock_path: &Path,
command: StartupCommand,
) -> RuntimeResult<StartupCommandExit> {
let client = AgentClient::connect(agent_sock_path)
.await
.map_err(|err| RuntimeError::Custom(format!("startup command connect: {err}")))?;
let request = ExecRequest {
cmd: command.cmd,
args: command.args,
env: command.env,
cwd: command.cwd,
user: command.user,
tty: false,
rows: 24,
cols: 80,
rlimits: Vec::new(),
};
let mut rx = client
.stream(TypedMessage::new(MessageType::ExecRequest, &request))
.await
.map_err(|err| RuntimeError::Custom(format!("startup command dispatch: {err}")))?;
while let Some(message) = rx
.recv()
.await
.map_err(|err| RuntimeError::Custom(format!("startup command stream: {err}")))?
{
match MessageType::from_wire_str(&message.t) {
Some(MessageType::ExecExited) => {
let exited = message
.payload::<ExecExited>()
.map_err(|err| RuntimeError::Custom(format!("startup command exit: {err}")))?;
return Ok(StartupCommandExit::Exited(exited.code));
}
Some(MessageType::ExecFailed) => {
let failed = message.payload::<ExecFailed>().map_err(|err| {
RuntimeError::Custom(format!("startup command failure: {err}"))
})?;
return Ok(StartupCommandExit::Failed(failed));
}
_ => {}
}
}
Err(RuntimeError::Custom(
"startup command stream ended before terminal event".into(),
))
}