blob: 1ce2825a985369e865125fd66ae2f3f9e4ebf99b [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::{self, Capability, CapabilityPath, ExposeDecl, ExposeSource, OfferDecl, OfferSource},
failure::format_err,
fidl_fuchsia_io::{
MODE_TYPE_DIRECTORY, MODE_TYPE_SERVICE, OPEN_RIGHT_READABLE, OPEN_RIGHT_WRITABLE,
},
fuchsia_zircon as zx,
std::sync::Arc,
};
/// Describes the source of a capability, as determined by `find_capability_source`
enum CapabilitySource {
/// This capability originates from the component instance for the given Realm.
/// point.
Component(Capability, Arc<Realm>),
/// This capability originates from component manager's namespace.
ComponentMgrNamespace(Capability),
/// This capability is an ambient service and originates from component manager itself.
AmbientService(CapabilityPath, Arc<Realm>),
}
/// `route_directory` will find the source of the directory capability used at `source_path` by
/// `absolute_moniker`, and pass along the `server_chan` to the hosting component's out
/// directory (or componentmgr's namespace, if applicable)
pub async fn route_directory<'a>(
model: &'a Model,
capability: &'a Capability,
abs_moniker: AbsoluteMoniker,
server_chan: zx::Channel,
) -> Result<(), ModelError> {
await!(route_capability(model, MODE_TYPE_DIRECTORY, capability, abs_moniker, server_chan))
}
/// `route_service` will find the source of the service capability used at `source_path` by
/// `absolute_moniker`, and pass along the `server_chan` to the hosting component's out
/// directory (or componentmgr's namespace, if applicable)
pub async fn route_service<'a>(
model: &'a Model,
capability: &'a Capability,
abs_moniker: AbsoluteMoniker,
server_chan: zx::Channel,
) -> Result<(), ModelError> {
await!(route_capability(model, MODE_TYPE_SERVICE, capability, abs_moniker, server_chan))
}
/// `route_capability` will find the source of the `capability` used by
/// `absolute_moniker`, and pass along the `server_chan` to the hosting component's out
/// directory (or componentmgr's namespace, if applicable) using an open request with
/// `open_mode`.
async fn route_capability<'a>(
model: &'a Model,
open_mode: u32,
capability: &'a Capability,
abs_moniker: AbsoluteMoniker,
server_chan: zx::Channel,
) -> Result<(), ModelError> {
let source = await!(find_capability_source(model, capability, abs_moniker))?;
let flags = OPEN_RIGHT_READABLE | OPEN_RIGHT_WRITABLE;
match source {
CapabilitySource::ComponentMgrNamespace(source_capability) => {
if let Some(path) = source_capability.path() {
io_util::connect_in_namespace(&path.to_string(), server_chan)
.map_err(|e| ModelError::capability_discovery_error(e))?;
} else {
return Err(ModelError::capability_discovery_error(format_err!(
"storage capabilities are not supported"
)));
}
}
CapabilitySource::Component(source_capability, realm) => {
if let Some(path) = source_capability.path() {
await!(Model::bind_instance_and_open(
&model,
realm,
flags,
open_mode,
path,
server_chan
))?;
} else {
return Err(ModelError::capability_discovery_error(format_err!(
"storage capabilities are not supported"
)));
}
}
CapabilitySource::AmbientService(source_capability_path, realm) => {
await!(AmbientEnvironment::serve(
model.ambient.clone(),
realm,
model.hooks.clone(),
&source_capability_path,
server_chan
))?;
}
}
Ok(())
}
/// Check if a used capability is ambient, and if so return the ambient `CapabilitySource`.
async fn find_ambient_capability<'a>(
model: &'a Model,
used_capability: &'a Capability,
abs_moniker: &'a AbsoluteMoniker,
) -> Result<Option<CapabilitySource>, ModelError> {
if let Some(path) = AMBIENT_SERVICES.iter().find(|p| match used_capability {
Capability::Service(s) => **p == s,
_ => false,
}) {
let realm = await!(model.look_up_realm(abs_moniker))?;
Ok(Some(CapabilitySource::AmbientService((*path).clone(), realm)))
} else {
Ok(None)
}
}
/// find_capability_source will walk the component tree to find the originating source of a
/// capability, starting on the given abs_moniker. It returns the absolute moniker of the
/// originating component, a reference to its realm, and the capability exposed or offered at the
/// originating source. If the absolute moniker and realm are None, then the capability originates
/// at the returned path in componentmgr's namespace.
async fn find_capability_source<'a>(
model: &'a Model,
used_capability: &'a Capability,
abs_moniker: AbsoluteMoniker,
) -> Result<CapabilitySource, ModelError> {
if let Some(ambient_capability) =
await!(find_ambient_capability(model, used_capability, &abs_moniker))?
{
return Ok(ambient_capability);
}
// Holds mutable state as we walk the tree
struct State {
// The capability as it's represented in the current component
capability: Capability,
// The moniker of the child we came from
child_moniker: Option<ChildMoniker>,
// The moniker of the component we are currently looking at
moniker: AbsoluteMoniker,
}
let moniker = match abs_moniker.parent() {
Some(m) => m,
None => return Ok(CapabilitySource::ComponentMgrNamespace(used_capability.clone())),
};
let mut s = State {
capability: used_capability.clone(),
child_moniker: abs_moniker.path().last().map(|c| c.clone()),
moniker: moniker,
};
// Walk offer chain
'offerloop: loop {
let current_realm = await!(model.look_up_realm(&s.moniker))?;
let realm_state = await!(current_realm.state.lock());
// This unwrap is safe because `look_up_realm` populates this field
let decl = realm_state.decl.as_ref().expect("missing offer decl");
let child_moniker = s.child_moniker.as_ref().unwrap();
if let Some(offer) =
decl.find_offer_source(&s.capability, child_moniker.name(), child_moniker.collection())
{
let source = match offer {
OfferDecl::Service(s) => &s.source,
OfferDecl::Directory(d) => &d.source,
OfferDecl::Storage(_) => {
return Err(ModelError::capability_discovery_error(format_err!(
"storage capabilities are not supported"
)))
}
};
match source {
OfferSource::Realm => {
// The offered capability comes from the realm, so follow the
// parent
s.capability = offer.clone().into();
s.child_moniker = s.moniker.path().last().map(|c| c.clone());
s.moniker = match s.moniker.parent() {
Some(m) => m,
None => {
return Ok(CapabilitySource::ComponentMgrNamespace(
s.capability.clone().into(),
))
}
};
continue 'offerloop;
}
OfferSource::Self_ => {
// The offered capability comes from the current component,
// return our current location in the tree.
return Ok(CapabilitySource::Component(
offer.clone().into(),
current_realm.clone(),
));
}
OfferSource::Child(child_name) => {
// The offered capability comes from a child, break the loop
// and begin walking the expose chain.
s.capability = offer.clone().into();
s.moniker = s.moniker.child(ChildMoniker::new(child_name.to_string(), None));
break 'offerloop;
}
}
} else {
return Err(ModelError::capability_discovery_error(format_err!(
"no matching offers found for capability {} from component {}",
s.capability,
s.moniker
)));
}
}
// Walk expose chain
loop {
let current_realm = await!(model.look_up_realm(&s.moniker))?;
let realm_state = await!(current_realm.state.lock());
// This unwrap is safe because look_up_realm populates this field
let decl = realm_state.decl.as_ref().expect("missing expose decl");
if let Some(expose) = decl.find_expose_source(&s.capability) {
let source = match expose {
ExposeDecl::Service(s) => &s.source,
ExposeDecl::Directory(d) => &d.source,
};
match source {
ExposeSource::Self_ => {
// The offered capability comes from the current component, return our
// current location in the tree.
return Ok(CapabilitySource::Component(
expose.clone().into(),
current_realm.clone(),
));
}
ExposeSource::Child(child_name) => {
// The offered capability comes from a child, so follow the child.
s.capability = expose.clone().into();
s.moniker = s.moniker.child(ChildMoniker::new(child_name.to_string(), None));
continue;
}
}
} else {
// We didn't find any matching exposes! Oh no!
return Err(ModelError::capability_discovery_error(format_err!(
"no matching exposes found for capability {} from component {}",
s.capability,
s.moniker
)));
}
}
}