mod machine;
use std::io::{Read, Write};
use std::net::Shutdown;
use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::Arc;
use std::thread;
pub use machine::Machine;
use super::{Paths, Resources, download};
const DATA_DISK: u64 = 128 * 1024 * 1024 * 1024;
pub fn install(paths: &Paths, archive: &[u8]) -> Result<(), String> {
download::unpack(archive, &paths.image)
}
fn pipe(mut from: UnixStream, mut to: UnixStream) {
let mut buffer = vec![0u8; 64 * 1024];
loop {
match from.read(&mut buffer) {
Ok(0) | Err(_) => break,
Ok(n) => {
if to.write_all(&buffer[..n]).is_err() {
break;
}
}
}
}
let _ = to.shutdown(Shutdown::Write);
}
fn join(client: UnixStream, engine: UnixStream) {
let (Ok(client_out), Ok(engine_out)) = (client.try_clone(), engine.try_clone()) else {
return;
};
let up = thread::spawn(move || pipe(client, engine_out));
pipe(engine, client_out);
let _ = up.join();
}
pub struct Running {
machine: Arc<Machine>,
}
impl Running {
pub fn start(paths: &Paths, resources: &Resources) -> Result<Self, String> {
if !paths.data.exists() {
std::fs::File::create(&paths.data)
.and_then(|f| f.set_len(DATA_DISK))
.map_err(|e| format!("{}: {e}", paths.data.display()))?;
}
let machine = Arc::new(Machine::new(paths, resources)?);
machine.start()?;
let _ = std::fs::remove_file(&paths.socket);
let listener = UnixListener::bind(&paths.socket)
.map_err(|e| format!("{}: {e}", paths.socket.display()))?;
let bridge = machine.clone();
thread::Builder::new()
.name("engine-socket".into())
.spawn(move || {
for client in listener.incoming().flatten() {
let machine = bridge.clone();
thread::spawn(move || match machine.connect() {
Ok(engine) => join(client, engine),
Err(_) => drop(client),
});
}
})
.map_err(|e| e.to_string())?;
Ok(Self { machine })
}
pub fn is_running(&self) -> bool {
self.machine.running()
}
pub fn stop(&self) {
self.machine.request_stop();
for _ in 0..50 {
if !self.machine.running() {
return;
}
thread::sleep(std::time::Duration::from_millis(100));
}
self.machine.force_stop();
}
}