1use std::ffi::OsStr;2use std::process::Stdio;3use std::sync::Mutex;45use bifrostlink::{Rpc, Rtt, WeakRpc};6use tokio::process::{Child, Command};78use remowt_link_shared::plugin::{Error, PluginEndpoints, PluginHost};9use remowt_link_shared::port::child_port;10use remowt_link_shared::{Address, BifConfig};1112pub fn serve(rpc: &mut Rpc<BifConfig>) {13 let host = Host {14 me: rpc.me(),15 rpc: rpc.clone().downgrade(),16 children: Mutex::new(Vec::new()),17 };18 PluginEndpoints(host).register_endpoints(rpc);19}2021struct Host {22 me: Address,23 rpc: WeakRpc<BifConfig>,24 children: Mutex<Vec<Child>>,25}2627impl Host {28 fn spawn(&self, id: u16, path: impl AsRef<OsStr>) -> Result<(), Error> {29 let rpc = self.rpc.clone().upgrade().ok_or(Error::Gone)?;3031 let mut child = Command::new(path)32 .arg(id.to_string())33 .arg(serde_json::to_string(&self.me).expect("address serializes"))34 .stdin(Stdio::piped())35 .stdout(Stdio::piped())36 .kill_on_drop(true)37 .spawn()38 .map_err(|e| Error::Spawn(e.to_string()))?;39 let stdin = child.stdin.take().expect("stdin piped");40 let stdout = child.stdout.take().expect("stdout piped");4142 let addr = Address::Plugin(id);43 rpc.add_direct(addr, child_port(stdout, stdin), Rtt(0));44 self.children.lock().expect("not poisoned").push(child);45 Ok(())46 }47}4849impl PluginHost for Host {50 async fn load_plugin(&self, id: u16, name: String) -> Result<(), Error> {51 52 53 54 if name.is_empty() || name == "." || name == ".." || name.contains(['/', '\0']) {55 return Err(Error::BadName);56 }57 let exe = std::env::current_exe().map_err(|e| Error::Spawn(e.to_string()))?;58 let dir = exe59 .parent()60 .ok_or_else(|| Error::Spawn("primary agent has no parent directory".to_owned()))?;61 self.spawn(id, dir.join(&name))62 }6364 async fn load_plugin_path(&self, id: u16, path: String) -> Result<(), Error> {65 if path.is_empty() || path.contains('\0') {66 return Err(Error::BadName);67 }68 self.spawn(id, path)69 }70}