use anyhow::{anyhow, Error};
use fidl::endpoints::{DiscoverableProtocolMarker, ServerEnd};
use fidl_fuchsia_component as fcomponent;
use fidl_fuchsia_component_decl as fdecl;
use fidl_fuchsia_component_runner as frunner;
use fidl_fuchsia_io as fio;
use fuchsia_component::client as fclient;
use rand::Rng;
const KERNEL_COLLECTION: &str = "kernels";
const CONTAINER_RUNNER_PROTOCOL: &str = "fuchsia.starnix.container.Runner";
pub struct StarnixKernel {
realm: fcomponent::RealmProxy,
pub name: String,
exposed_dir: fio::DirectoryProxy,
}
impl StarnixKernel {
pub async fn create(
realm: fcomponent::RealmProxy,
kernel_url: &str,
start_info: frunner::ComponentStartInfo,
controller: ServerEnd<frunner::ComponentControllerMarker>,
) -> Result<Self, Error> {
let kernel_name = generate_kernel_name(&start_info)?;
realm
.create_child(
&fdecl::CollectionRef { name: KERNEL_COLLECTION.into() },
&fdecl::Child {
name: Some(kernel_name.clone()),
url: Some(kernel_url.to_string()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
Default::default(),
)
.await?
.map_err(|e| anyhow::anyhow!("failed to create kernel: {:?}", e))?;
let exposed_dir = open_exposed_directory(&realm, &kernel_name, KERNEL_COLLECTION).await?;
let container_runner = fclient::connect_to_named_protocol_at_dir_root::<
frunner::ComponentRunnerMarker,
>(&exposed_dir, CONTAINER_RUNNER_PROTOCOL)?;
container_runner.start(start_info, controller)?;
Ok(Self { realm, name: kernel_name, exposed_dir })
}
pub fn connect_to_protocol<P: DiscoverableProtocolMarker>(&self) -> Result<P::Proxy, Error> {
fclient::connect_to_protocol_at_dir_root::<P>(&self.exposed_dir)
}
pub async fn destroy(&self) -> Result<(), Error> {
self.realm
.destroy_child(&fdecl::ChildRef {
name: self.name.clone(),
collection: Some(KERNEL_COLLECTION.into()),
})
.await?
.map_err(|e| anyhow::anyhow!("failed to destory kernel: {:?}", e))?;
Ok(())
}
}
fn generate_kernel_name(_start_info: &frunner::ComponentStartInfo) -> Result<String, Error> {
let random_id: String = rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(7)
.map(char::from)
.collect();
Ok(random_id)
}
async fn open_exposed_directory(
realm: &fcomponent::RealmProxy,
child_name: &str,
collection_name: &str,
) -> Result<fio::DirectoryProxy, Error> {
let (directory_proxy, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
realm
.open_exposed_dir(
&fdecl::ChildRef { name: child_name.into(), collection: Some(collection_name.into()) },
server_end,
)
.await?
.map_err(|e| {
anyhow!(
"failed to bind to child {} in collection {:?}: {:?}",
child_name,
collection_name,
e
)
})?;
Ok(directory_proxy)
}