blob: 1c7a26340c9fc8968bccba7366bfe276f3b2fe56 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::model::*,
cm_rust::data,
cm_rust::CapabilityPath,
failure::format_err,
fidl::endpoints::{Proxy, ServerEnd},
fidl_fuchsia_io::DirectoryProxy,
fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync, fuchsia_zircon as zx,
futures::{
future::{join_all, BoxFuture, FutureObj},
lock::Mutex,
},
std::sync::Arc,
};
// Hooks into the component model implement this trait.
// TODO(fsamuel): It's conceivable that as we add clients and event types,
// many clients may be interested in just a small subset of events but they'd
// have to implement all the functions in this trait. Alternatively, we can
// break down each event type into a separate trait so that clients can pick
// and choose which events they'd like to monitor.
pub trait Hook {
// Called when a component instance is bound to the given `realm`.
fn on_bind_instance<'a>(
&'a self,
realm: Arc<Realm>,
realm_state: &'a RealmState,
route_fn_factory: Arc<dyn CapabilityRoutingFnFactory + Send + Sync>,
) -> BoxFuture<Result<(), ModelError>>;
// Called when a dynamic instance is added with `realm`.
fn on_add_dynamic_child(&self, realm: Arc<Realm>) -> BoxFuture<Result<(), ModelError>>;
}
pub type Hooks = Vec<Arc<dyn Hook + Send + Sync + 'static>>;
/// Parameters for initializing a component model, particularly the root of the component
/// instance tree.
pub struct ModelParams {
/// The ambient environment.
pub ambient: Box<dyn AmbientEnvironment>,
/// The URL of the root component.
pub root_component_url: String,
/// The component resolver registry used in the root realm.
/// In particular, it will be used to resolve the root component itself.
pub root_resolver_registry: ResolverRegistry,
/// The default runner used in the root realm (nominally runs ELF binaries).
pub root_default_runner: Box<dyn Runner + Send + Sync + 'static>,
/// A set of hooks into key events of the Model.
pub hooks: Hooks,
}
/// The component model holds authoritative state about a tree of component instances, including
/// each instance's identity, lifecycle, capabilities, and topological relationships. It also
/// provides operations for instantiating, destroying, querying, and controlling component
/// instances at runtime.
///
/// To facilitate unit testing, the component model does not directly perform IPC. Instead, it
/// delegates external interfacing concerns to other objects that implement traits such as
/// `Runner` and `Resolver`.
#[derive(Clone)]
pub struct Model {
pub root_realm: Arc<Realm>,
pub ambient: Arc<dyn AmbientEnvironment>,
pub hooks: Arc<Hooks>,
}
impl Model {
/// Creates a new component model and initializes its topology.
pub fn new(params: ModelParams) -> Model {
Model {
ambient: params.ambient.into(),
root_realm: Arc::new(Realm {
resolver_registry: Arc::new(params.root_resolver_registry),
default_runner: Arc::new(params.root_default_runner),
abs_moniker: AbsoluteMoniker::root(),
component_url: params.root_component_url,
// Started by main().
startup: fsys::StartupMode::Lazy,
state: Mutex::new(RealmState { execution: None, child_realms: None, decl: None }),
}),
hooks: Arc::new(params.hooks),
}
}
/// Binds to the component instance with the specified moniker, causing it to start if it is
/// not already running. Also binds to any descendant component instances that need to be
/// eagerly started.
pub async fn look_up_and_bind_instance(
&self,
abs_moniker: AbsoluteMoniker,
) -> Result<(), ModelError> {
let realm: Arc<Realm> = await!(self.look_up_realm(&abs_moniker))?;
let eager_children = await!(self.bind_instance(realm.clone()))?;
await!(self.bind_eager_children_recursive(eager_children))?;
Ok(())
}
/// Given a realm and path, lazily bind to the instance in the realm, open, then bind its eager
/// children.
pub async fn bind_instance_and_open<'a>(
&'a self,
realm: Arc<Realm>,
flags: u32,
open_mode: u32,
path: &'a CapabilityPath,
server_chan: zx::Channel,
) -> Result<(), ModelError> {
let eager_children = {
let eager_children = await!(self.bind_instance(realm.clone()))?;
let server_end = ServerEnd::new(server_chan);
let state = await!(realm.state.lock());
let out_dir = &state
.execution
.as_ref()
.ok_or(ModelError::capability_discovery_error(format_err!(
"component hosting capability isn't running: {}",
realm.abs_moniker
)))?
.outgoing_dir;
let path = io_util::canonicalize_path(&path.to_string());
out_dir.open(flags, open_mode, &path, server_end).expect("failed to send open message");
eager_children
};
await!(self.bind_eager_children_recursive(eager_children))?;
Ok(())
}
/// Looks up a realm by absolute moniker. The component instance in the realm will be resolved
/// if that has not already happened.
pub async fn look_up_realm<'a>(
&'a self,
look_up_abs_moniker: &'a AbsoluteMoniker,
) -> Result<Arc<Realm>, ModelError> {
let mut cur_realm = self.root_realm.clone();
for moniker in look_up_abs_moniker.path().iter() {
cur_realm = {
await!(cur_realm.resolve_decl())?;
let cur_state = await!(cur_realm.state.lock());
let child_realms = cur_state.child_realms.as_ref().unwrap();
if !child_realms.contains_key(&moniker) {
return Err(ModelError::instance_not_found(look_up_abs_moniker.clone()));
}
child_realms[moniker].clone()
}
}
await!(cur_realm.resolve_decl())?;
Ok(cur_realm)
}
// Populates the RealmState struct and starts the component instance.
async fn populate_realm_state<'a>(
&'a self,
state: &'a mut RealmState,
realm: Arc<Realm>,
) -> Result<Vec<Arc<Realm>>, ModelError> {
if state.execution.is_some() {
return Ok(vec![]);
}
let component = await!(realm.resolver_registry.resolve(&realm.component_url))?;
state.populate_decl(component.decl, &*realm)?;
let decl = state.decl.as_ref().expect("ComponentDecl unavailable.");
if decl.program.is_some() {
let (outgoing_dir_client, outgoing_dir_server) =
zx::Channel::create().map_err(|e| ModelError::namespace_creation_failed(e))?;
let (runtime_dir_client, runtime_dir_server) =
zx::Channel::create().map_err(|e| ModelError::namespace_creation_failed(e))?;
let mut namespace = IncomingNamespace::new(component.package)?;
let ns = await!(namespace.populate(self.clone(), &realm.abs_moniker, decl))?;
let execution = Execution::start_from(
component.resolved_url,
namespace,
DirectoryProxy::from_channel(
fasync::Channel::from_channel(outgoing_dir_client).unwrap(),
),
DirectoryProxy::from_channel(
fasync::Channel::from_channel(runtime_dir_client).unwrap(),
),
)?;
let start_info = fsys::ComponentStartInfo {
resolved_url: Some(execution.resolved_url.clone()),
program: data::clone_option_dictionary(&decl.program),
ns: Some(ns),
outgoing_dir: Some(ServerEnd::new(outgoing_dir_server)),
runtime_dir: Some(ServerEnd::new(runtime_dir_server)),
};
await!(realm.default_runner.start(start_info))?;
state.execution = Some(execution);
}
// Return children that need eager starting.
let mut eager_children: Vec<Arc<Realm>> = Vec::new();
for child_realm in
state.child_realms.as_ref().expect("Unable to access child realms.").values()
{
match child_realm.startup {
fsys::StartupMode::Eager => {
eager_children.push(child_realm.clone());
}
fsys::StartupMode::Lazy => {}
}
}
Ok(eager_children)
}
/// Binds to the component instance in the given realm, starting it if it's not
/// already running. Returns the list of child realms whose instances need to be eagerly started
/// after this function returns. The caller is responsible for calling
/// bind_eager_children_recursive themselves to ensure eager children are recursively binded.
async fn bind_instance<'a>(&'a self, realm: Arc<Realm>) -> Result<Vec<Arc<Realm>>, ModelError> {
let mut state = await!(realm.state.lock());
let eager_children = await!(self.populate_realm_state(&mut *state, realm.clone()))?;
let route_fn_factory = Arc::new(ModelCapabilityRoutingFnFactory::new(&self));
for hook in self.hooks.iter() {
await!(hook.on_bind_instance(realm.clone(), &*state, route_fn_factory.clone()))?;
}
Ok(eager_children)
}
/// Binds to a list of instances, and any eager children they may return.
async fn bind_eager_children_recursive<'a>(
&'a self,
mut instances_to_bind: Vec<Arc<Realm>>,
) -> Result<(), ModelError> {
loop {
if instances_to_bind.is_empty() {
break;
}
let futures: Vec<_> = instances_to_bind
.iter()
.map(|realm| {
FutureObj::new(Box::new(
async move { await!(self.bind_instance(realm.clone())) },
))
})
.collect();
let res = await!(join_all(futures));
instances_to_bind.clear();
for e in res {
instances_to_bind.append(&mut e?);
}
}
Ok(())
}
}