blob: 0876ee4ac1e8b801781e604af2c49adc5efaa15c [file] [log] [blame] [view]
# Connect components
This document demonstrates how to connect [components][glossary.component]
together using capabilities and additional tools for parent components to manage
their children.
## Concepts
You should understand the following concepts before continuing with this guide:
* The Component Framework assembles the [namespace][glossary.namespace] for a
component using [component declarations][glossary.component-declaration] that
describe the [capabilities][glossary.capability] the component requires to
function. The capabilities the component exposes to others are assembled into
an [exposed directory][glossary.exposed-directory].
* Every component receives a handle to the server end of a
[`Directory`][fidl-fuchsia.io.Directory] channel called the
[outgoing directory][glossary.outgoing-directory]. The component's program
makes discoverable any capabilities that it provides through this directory.
* At runtime, the [component instance tree][glossary.component-instance-tree]
describes parent and child relationships between
[component instances][glossary.component-instance]. The component instance
tree and the capability routes over that tree are collectively referred to
as the [component topology][glossary.component-topology].
* Parent components declare child components either statically in their
[component manifest][glossary.component-manifest] or dynamically using a
[component collection][glossary.component-collection]. A collection is a
container for dynamic children that may be created and destroyed at runtime
using the `fuchsia.component.Realm` framework protocol.
For more details on these concepts, see [Realms][doc-realms] and
[Capabilities][doc-capabilities].
## Connecting capabilities through routing {#capabilities}
Note: For a complete example using routed capabilities, see
[`//examples/components/routing`][example-routing].
Components interact with each other through capabilities. Capabilities served by
a component need to be declared in that component's manifest and, for them to be
usable by others, routed to parent/child components through `expose` and `offer`
declarations. Other components that use that capability also need to declare
their use in their manifests. In order for their programs to use the capability
at runtime, the used capability must be routed to that component - either
offered from a parent or exposed by a child.
*Capability routing* refers to the recursive process, performed by the component
manager, of identifying a serving component by following individual routing
steps described in manifest files. Capability routing is initiated when:
* A component's program opens a path in its [namespace][glossary.namespace].
* A component's program opens a path in another component's
[exposed directory][glossary.exposed-directory].
* A developer invokes `ffx component route`.
* Starting a component that depends on a Resolver or Runner whose backing
resolver or runner capabilities have not been routed.
Routing is performed lazily: while a capability may be *configured* to be
provided by a parent or child (directly or indirectly through further
delegation), the target component may not have been resolved yet when the
routing operation is initiated. Practically, this means that the full route from
the requesting component to the final serving component may not be known until
routing is attempted.
### Provide a capability implementation {#provide-capability}
Components that provide a capability must declare the capability in their
component manifest using a [`capabilities`][cml-capabilities] declaration.
See the following example that declares a FIDL
[protocol capability][doc-protocol] in the providing component's manifest:
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/rust/echo_server/meta/echo_server.cml" region_tag="example_snippet" adjust_indentation="auto" highlight="16,17,18,19" %}
```
At runtime, the server component provides an implementation of the capability by
serving it in its outgoing directory using the [fuchsia.io][fidl-fuchsia.io]
protocol. The generated FIDL bindings wrap this handle and enable the provider
to begin receiving incoming requests:
* {Rust}
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/rust/echo_server/src/main.rs" region_tag="main_body" adjust_indentation="auto" highlight="1,2,3,4,8,14,15,16,21,22,23,24,25,26,27,28" %}
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/rust/echo_server/src/main.rs" region_tag="handler" adjust_indentation="auto" highlight="1,2,3,4,5,6,7" %}
```
* {C++}
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/cpp/echo_server/main.cc" region_tag="handler" adjust_indentation="auto" highlight="1,2,3,4,5,6" %}
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/cpp/echo_server/main.cc" region_tag="main_body" adjust_indentation="auto" highlight="3,9,10,11,12,13,14,15,16,17" %}
```
### Connect to routed capabilities {#connect-routes}
Client components declare capabilities they may request in their component
manifest with a [`use`][cml-use] declaration.
See the following example of a client component's manifest that uses the FIDL
protocol provided by the previous component:
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/rust/echo_client/meta/echo_client.cml" region_tag="example_snippet" adjust_indentation="auto" highlight="19,20,21,22" %}
```
At runtime, the client component opens paths populated in its namespace using
the [fuchsia.io][fidl-fuchsia.io] protocol in order to acquire capabilities
provided by other components. The Fuchsia component library works with the
generated FIDL bindings to provide a structured interface for communicating over
the channel:
* {Rust}
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/rust/echo_client/src/main.rs" region_tag="main_body" adjust_indentation="auto" highlight="7,8,9,10,11,12,13,14" %}
```
* {C++}
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/cpp/echo_client/main.cc" region_tag="main_body" adjust_indentation="auto" highlight="2,3,4,5,7,8,10" %}
```
It is the responsibility of the component's parent to route to it all necessary
capabilities.
#### Mark some used capabilities as optional {#optional-use}
Not all capabilities used by a component are required for it to operate
successfully. Sometimes a component can still execute without issue if a
capability is missing, and its presence will merely enable some additional or
alternative behavior.
To enable the component framework to understand which capabilities a component
requires and which capabilities are optional for a component, use the
`availability` field.
```
use: [
{
// It is ok if this protocol is unavailable
protocol: "fuchsia.examples.Echo1",
availability: "optional",
},
{
// This protocol MUST be provided for the component to function correctly.
protocol: "fuchsia.examples.Echo2",
availability: "required",
},
]
```
If a component has a `required` use declaration for a capability (the default)
but its parent offers the capability as `optional`, then the
[static capability analyzer][static-analyzer] will generate an error and
connection attempts at runtime will always fail.
##### Consuming optional capabilities {#consuming-optional-capabilities}
In the case that a component's parent has `offer`ed a capabilitity with
`availability: "optional"`, the capability may not be usable at runtime.
An entry in the component's [namespace][glossary.namespace] will be present
whether the capability is available or not. Any attempt to open the path for
that capability will result in the handle provided to the `Directory.Open()`
call being closed with a `ZX_ERR_NOT_FOUND` epitaph.
Usage of `libc` methods like `open()` or `stat()` will return `ENOENT`.
### Route capabilities {#route-capability}
Components may only access capabilities routed to them. Capabilities can
originate from anywhere in the component topology as long as a valid capability
route exists as a chain of the following declarations from the capability
provider to any consumers:
* [`expose`][cml-expose]: Routes a capability up to the component's parent.
* [`offer`][cml-offer]: Routes a capability down to one of the component's
children.
To connect capability providers with components requesting those capabilities,
do the following:
1. Add an `offer` or `expose` declaration to the capability provider component:
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/rust/echo_server/meta/echo_server.cml" region_tag="example_snippet" adjust_indentation="auto" highlight="20,21,22,23,24,25" %}
```
1. For each intermediate component in the component instance tree, include
additional `expose` and `offer` declarations until you reach the consuming
component containing a `use` declaration:
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/routing/meta/echo_realm.cml" region_tag="example_snippet" highlight="13,14,15,16,17,18,19,21,22,23,24,25,26,27,28,29,30" %}
```
#### Optional dependencies {#optional-offer}
When a component has an optional dependency on a capability, it is then up to
that component's parent to decide if the component will receive that capability
or not. When offering a capability, a component may set the `availability` field
to either `optional`, `required`, or `same_as_target`. Each value has the
following semantics:
* `optional`: The target of the offer must declare its ability to handle the
absence of this capability by marking it's `use` declaration as `optional`.
If the target cannot do this, (i.e. the target has an availability of
`required` for this capability), then routing the capability will cause an
error.
* `required`: The target must receive this capability. If the offer source is
`parent` and the component's parent (the target's grandparent) offered this
as an optional capability, then routing the capability will cause an error
because the parent cannot guarantee the capability's availability.
* `same_as_target`: The availability of this capability is determined by the
target's expectations. If the target has an optional dependency on this
capability, then this offer will also be optional. If the target has a
required dependency on this capability, then this offer will also be
required.
```json5
offer: [
{
// child-1 MUST receive the protocol 'fuchsia.logger.LogSink'.
protocol: "fuchsia.logger.LogSink",
to: "#child-1",
from: "#child-2",
availability: "required",
},
{
// child-1 MUST be able to handle the absence of the protocol
// 'fuchsia.tracing.provider.Registry'.
protocol: "fuchsia.tracing.provider.Registry",
to: "#child-1",
from: "parent",
availability: "optional",
},
{
// child-1 decides if it must receive the protocol
// 'fuchsia.posix.socket.Provider', or if it must be able to support its
// absence.
protocol: "fuchsia.posix.socket.Provider",
to: "#child-1",
from: "parent",
availability: "same_as_target",
},
]
```
Like with use declarations, the `availability` field may be omitted, in which
case it defaults to `required`.
#### Transitionally dependencies {#transitional-offer}
The component framework allows components to soft-transition into using and
offering both optional and required capabilities. With the transitional
availability marker, a component using a capability, will not cause Scrutiny
validation errors whether or not the parent is required, optional, or same as
target. Note, though this field exists to enable soft-transitions, components
should ultimately settle on either optional or required.
To use this feature, the child component will mark its availability as
"transitional":
```json5
use: [
{
// It is ok if this protocol is offered as "required" or "optional"
protocol: "fuchsia.examples.Echo",
availability: "transitional",
},
]
```
## Managing child components {#children}
Note: For a complete example using child components, see
[`//examples/components/lifecycle`][example-lifecycle].
Components can interact with each other from anywhere in the component topology
through capabilities as long as a valid capability route exists between them.
There are additional methods that enable parent components to interact with
their direct children.
The following example component declares a single static child named `lifecycle`
and a collection named `echo` where additional child components may be created
at runtime:
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/meta/manager.cml" region_tag="example_snippet" adjust_indentation="auto" %}
```
Notice that a collection behaves like a static child instance in the parent
component's manifest — you can give it a name and offer specific capabilities to
it. All child components in the collection may access the set of capabilities
offered to it.
### Start child components {#start-child}
The Component Framework provides the [`fuchsia.component.Binder`][fidl-Binder]
protocol for parent components to explicitly start a child that may not expose
any other capabilities. Since this capability is provided by the framework,
child components only need to expose it from their component manifest:
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/meta/lifecycle.cml" region_tag="example_snippet" adjust_indentation="auto" %}
```
### Create dynamic children {#create-child}
To create a new child component at runtime, use the
[`fuchsia.component.Realm`][fidl-Realm] protocol to create the component inside
of an existing collection. Call the [`CreateChild`][fidl-Realm.CreateChild]
method with the following parameters:
* [`CollectionRef`][fidl-decl.CollectionRef]: Describes the collection where
the component will be added.
* [`Child`][fidl-decl.Child]: Component declaration, including its name and
component URL.
* {Rust}
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/manager.rs" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/manager.rs" region_tag="create_child" adjust_indentation="auto" %}
```
* {C++}
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/manager.cc" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/manager.cc" region_tag="create_child" adjust_indentation="auto" %}
```
### Connect to child capabilities {#connect-child}
Because the parent of a dynamic component is not known at build time, its
exposed capabilities cannot be named in capability routes expressed in the
component manifest.
To connect with the capabilities exposed by a dynamic child instance:
1. Use the [`fuchsia.component.Realm`][fidl-Realm] protocol to open the child's
exposed directory. Call the [`OpenExposedDir`][fidl-Realm.OpenExposedDir]
method with the child component's name and the collection name:
* {Rust}
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/manager.rs" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/manager.rs" region_tag="connect_child" adjust_indentation="auto" %}
```
* {C++}
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/manager.cc" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/manager.cc" region_tag="connect_child" adjust_indentation="auto" %}
```
2. Connect to the child's exposed capabilities using the exposed directory
handle as the root:
* {Rust}
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/manager.rs" region_tag="echo_send" adjust_indentation="auto" %}
```
* {C++}
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/manager.cc" region_tag="echo_send" adjust_indentation="auto" %}
```
### Destroy dynamic children {#destroy-child}
When the dynamic child is no longer needed, use the
[`fuchsia.component.Realm`][fidl-Realm] protocol to destroy the component
instance. Call the [`DestroyChild`][fidl-Realm.DestroyChild] method with a
[`ChildRef`][fidl-decl.ChildRef] representing the child inside the collection.
* {Rust}
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/manager.rs" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/manager.rs" region_tag="destroy_child" adjust_indentation="auto" %}
```
* {C++}
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/manager.cc" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/manager.cc" region_tag="destroy_child" adjust_indentation="auto" %}
```
This causes the component to stop if it is currently running. To handle this
event in your component, see [listen for stop events](#lifecycle-notifications).
## Controlling component lifecycle {#lifecycle}
The Component Framework provides features to modify and interact with various
parts of the component lifecycle.
For more details on lifecycle concepts, see
[Component lifecycle][doc-lifecycle].
### Lifecycle notifications {#lifecycle-notifications}
The ELF runner notifies components of lifecycle events using the
[`fuchsia.process.lifecycle.Lifecycle`][fidl-Lifecycle] protocol.
To listen for stop notifications in your child component:
1. Subscribe to the [lifecycle event][elf-lifecycle] in your component
manifest:
* {Rust}
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/meta/lifecycle.cml" region_tag="lifecycle_event" adjust_indentation="auto" highlight="9,10" %}
```
* {C++}
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/meta/lifecycle.cml" region_tag="lifecycle_event" adjust_indentation="auto" highlight="9,10" %}
```
1. Register a lifecycle handler using the startup handle provided by the
runner:
* {Rust}
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/lifecycle.rs" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/rust/src/lifecycle.rs" region_tag="lifecycle_handler" adjust_indentation="auto" %}
```
* {C++}
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/lifecycle.cc" region_tag="imports" adjust_indentation="auto" %}
// ...
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/lifecycle/cpp/lifecycle.cc" region_tag="lifecycle_handler" adjust_indentation="auto" %}
```
### Start with parent {#eager}
[Component manifests][doc-manifests] let you mark a child as
[`eager`][cml-children], which causes the component framework to implicitly
start that child with the parent.
If the eager child fails to start for any reason (such as a missing component),
component manager exhibits the following behavior:
* If the parent is not the root component, the parent will start but the
component that bound to it will observe a dropped connection (just like any
other failed binding).
* If the parent is the root component, component manager will crash, with an
error message like:
```none {:.devsite-disable-click-to-copy}
[component_manager] ERROR: protocol `fuchsia.component.CoreBinder` was not available for target `startup`:
failed to resolve `fuchsia-pkg://fuchsia.com/your_component#meta/your_component.cm`:
package not found: remote resolver responded with PackageNotFound
For more, run `ffx component doctor `startup`
```
Components marked as `eager` can cause system crashes when they are not present
if their ancestors are also marked `eager` up to the root component. This is
important because many build configurations create system images containing a
subset of the available components. To avoid this problem, declare these
components using [**core realm shards**][core-shard] to ensure they can be
safely excluded from test builds and product images.
An `eager` component should also be in the same [package set][doc-package-set]
as its parent since the component will be started at the same time as its
parent. Typically, `eager` components should be in the product's base package
set.
To determine if your package is in the base package set, run the following
command:
```posix-terminal
fx list-packages --verbose {{ '<var label="package name">my-package</var>' }}
```
This command outputs a list of the package sets where the matching package is
found. For example, `system-update-checker` is in the `base` and `universe`
package sets:
```none {:.devsite-disable-click-to-copy}
$ fx list-packages --verbose system-update-checker
system-update-checker [base universe]
```
You can also look at all the packages in the base package set using the `--base`
option:
```posix-terminal
fx list-packages --base
```
### Reboot on terminate {#reboot-on-terminate}
[Component manifests][doc-manifests] let you control the termination policy of
your component using [`on_terminate`][cml-children]. Components with the
"reboot-on-terminate" policy set cause the system to gracefully reboot if the
component terminates for any reason other than successful exit.
Note: This is a special feature intended for use only by system components
deemed critical to the system's function. Therefore, its use is governed by a
security policy allowlist.
To enable this feature, do the following:
1. Mark the child as `on_terminate: "reboot"` in the parent's component manifest:
```json5
// core.cml
{
children: [
...
{
name: "system-update-checker",
url: "fuchsia-pkg://fuchsia.com/system-update-checker#meta/system-update-checker.cm",
startup: "eager",
{{ '<strong>' }}on_terminate: "reboot",{{ '</strong>' }}
},
],
}
```
1. Add the component's moniker to component manager's security policy allowlist
at
[`//src/security/policy/component_manager_policy.json5`][src-security-policy]:
```json5
// //src/security/policy/component_manager_policy.json5
{
security_policy: {
...
child_policy: {
reboot_on_terminate: [
...
"/core/system-update-checker",
],
},
},
}
```
## Errors {#errors}
### Signaling mechanism
When routing is initiated by a requesting client by performing a
[`Directory.Open()`][fidl-fuchsia.io.Directory] request on its namespace, or on
the [exposed directory][glossary.exposed-directory], it passes the server-end
handle of a zircon object that will provide the capability once routed. Errors
will result in that handle's object being closed with an epitaph. The epitaph
payload is always a Zircon status code.
Since routing is lazy and *asynchronous*, this message may arrive at any time
after the routing operation is initiated.
> NOTE: once routing has *succeeded*, the serving component can *also* close the
> same object with a status code of their choice. It is impossible for a client
> to discern if the object was closed by component manager or by the serving
> component, or another party delegated to thereafter.
The same error signaling mechanism is used for `libc`-like calls like `open()`.
See the section [Troubleshooting](#troubleshooting) for practical examples.
### Error status codes
The following error codes may be sent by component manager to indicate a failed
routing operation:
* `ZX_ERR_NOT_FOUND`: the capability was unable to be routed for one of the
following reasons:
* An optional capability was not provided in this configuration.
* A configuration error in any one of the components along the route path.
* The serving component is incompatible with this version of Fuchsia.
* A bug in the program of the serving component.
* A bug in any of the [Resolvers][doc-resolvers] or [Runners][doc-runners]
involved in completing the routing operation.
* `ZX_ERR_ACCESS_DENIED`: the capability was unable to be routed because the
requesting component is not allowed to access it. For example:
* A [policy allow-list][src-security-policy] for the capability exists but
does not include the requesting component.
* The requesting component asked for rights greater than what was provided
to it (ie, asking for read/write on a directory provided as read-only).
* `ZX_ERR_TIMED_OUT`: one of the routing steps timed out.
* `ZX_ERR_INTERNAL`: component manager itself encountered an unexpected error,
indicating a bug in the platform.
`NOT_FOUND`, `ACCESS_DENIED`, and `INTERNAL` errors will reproduce for the same
capability so long as no software on the platform is updated. A software update,
even for a single component, can change a capability's route and might affect
the availability of that capability.
### Principles of routing error semantics
* *Minimality*: since the error signaling path is shared between component
manager and the serving component, component manager leaves the majority of
the error space for the serving component to utilize.
* *Client perspective*: while routing depends on many individual
sub-operations that can each fail for a variety of reasons including errors
on the part of other component authors, the error semantics are tailored to
the requesting client and the needs of the requesting client. For example,
user-error on the part of an intermediate component author will still return
`NOT_FOUND` for the requesting client.
## Troubleshooting {#troubleshooting}
This section contains common issues you may encounter trying to `use` and
connect to capabilities from your component with suggested solutions.
### Capability routing failed {#troubleshoot-use-routing}
Component manager performs [capability routing][doc-capabilities] to find the
source of a given capability once your component attempts to access the
capability. Routing can fail if one of the component manifests in the routing
path is configured incorrectly. For example, an `offer` or `expose` declaration
is missing from some component in the path, or one of the components in the
chain could not be resolved.
Do the following to check if a routing failure was the cause of channel closure:
* Use `ffx component route` to check the routing for your component. This
can either be used with the moniker of your component or your component's
URL. For example:
```bash
# check with the moniker
ffx component route /core/ffx-laboratory:echo_realm/echo_client
# check with a partial URL
ffx component route echo_client.cm
```
* Check the component logs with `ffx log` for a message beginning with `Failed
to route` that explains where the routing chain failed. For example:
```none {:.devsite-disable-click-to-copy}
[echo_client][][W] protocol `fidl.examples.routing.echo.Echo` was not available for target `/core/ffx-laboratory:echo_realm/echo_client`:
`fidl.examples.routing.echo.Echo` was not offered to `/core/ffx-laboratory:echo_realm/echo_client` by parent
For more, run `ffx component doctor /core/ffx-laboratory:echo_realm/echo_client`
```
* Check for an [epitaph on the closed channel](#troubleshooting). The epitaph
set for the most common routing failures is `ZX_ERR_NOT_FOUND`:
```none {:.devsite-disable-click-to-copy}
[echo_client][][I] Connecting to Echo protocol failed with error
"A FIDL client's channel to the service fidl.examples.routing.echo.Echo was closed: NOT_FOUND"
```
See [routing errors](#errors) for more.
For a self-contained example of failed capability routing, see
[`//examples/components/routing_failed`][example-routing-failed].
### Receiving errors from routing
When capability routing fails, the underlying FIDL channel closes. FIDL protocol
bindings return an error status if the channel was closed. Consider the
following example:
* {Rust}
```rust
let echo = connect_to_protocol::<EchoMarker>()
.context("Failed to connect to echo service")?;
let res = echo.echo_string(Some("Hippos rule!")).await;
match res {
Ok(_) => { info!("Call succeeded!"); }
{{ '<strong>' }}Err(fidl::Error::ClientChannelClosed { status, service_name } => { {{ '</strong>' }}
{{ '<strong>' }}error!("Channel to service {} was closed with status: {}", service_name, status); {{ '</strong>' }}
{{ '<strong>' }}} {{ '</strong>' }}
Err(e) => {
error!("Unexpected error: {}", e);
}
};
```
* {C++}
```cpp
fuchsia::examples::EchoPtr echo_proxy;
auto context = sys::ComponentContext::Create();
context->svc()->Connect(echo_proxy.NewRequest());
{{ '<strong>' }}// Sets an error handler that will be called if an error causes the underlying {{ '</strong>' }}
{{ '<strong>' }}// channel to be closed. {{ '</strong>' }}
{{ '<strong>' }}echo_proxy.set_error_handler([&loop](zx_status_t status) { {{ '</strong>' }}
{{ '<strong>' }}printf("Channel was closed with status: %d\n", status); {{ '</strong>' }}
{{ '<strong>' }}// ... {{ '</strong>' }}
{{ '<strong>' }}}); {{ '</strong>' }}
echo_proxy->EchoString("Hippos rule!", [&](std::string response) {
// ...
});
```
Note: If the protocol method doesn't return a value (such as a one-way method),
the error status is only set if the channel was closed prior to the method call.
To determine the underlying cause of a channel closure, you can inspect the
optional [epitaph][doc-epitaphs] set on the channel. To retrieve the epitaph on
a closed channel, do the following:
* {Rust}
```rust
let stream = echo.take_event_stream();
match stream.next().await {
Some(Err(fidl::Error::ClientChannelClosed { status, .. })) => {
info!("Channel was closed with epitaph: {}", status);
}
Some(m) => {
info!("Received message other than epitaph or peer closed: {:?}", m);
}
None => {
info!("Component failed to start or channel was closed by server");
}
}
```
* {C++}
```cpp
echo_proxy.set_error_handler([&loop](zx_status_t status) {
// If an Epitaph was present on the channel, its error value will be passed as
// the parameter.
printf("Channel was closed with epitaph: %d\n", status);
});
```
### Component failed to start {#troubleshoot-use-start}
You may encounter an error if capability routing was successful, but an issue
occurred resolving or starting the component. The form of the error message
depends on the [component runner][doc-runners]:
* For the ELF runner, check the component manager logs with `ffx log --filter
component_manager`. Look for a message starting with `Failed to start
component`. For example:
```none {:.devsite-disable-click-to-copy}
[component_manager] WARN: Failed to start component
`fuchsia-pkg://fuchsia.com/components-routing-failed-example#meta/echo_server_bad.cm`:
unable to load component with url "fuchsia-pkg://fuchsia.com/components-routing-failed-example#meta/echo_server_bad.cm":
error loading executable: "reading object at \"bin/routing_failed_echo_server_oops\" failed:
A FIDL client's channel to the service fuchsia.io.File was closed: PEER_CLOSED"
```
* For other runners, check the [logs][doc-logs] of the runner component. You
can do this by running the following command:
```posix-terminal
ffx log --tags {{ '<var label="component runner">runner-name</var>' }}
```
To address the issue, verify the following:
* The [`program`][cml-program] declaration in your component manifest is
configured properly. For example, verify that the binary's path is spelled
correctly.
* The binary itself and all other resource needed to start the component are
included in the [package][doc-packages].
For an example of a component that failed to start due to a misconfigured
component manifest, see
[`//examples/components/routing_failed`][example-routing-failed].
### Component terminated or closed the channel {#troubleshoot-use-terminated}
If you have verified that routing succeeded and the component started
successfully, you may be experiencing an issue where the source component closed
the channel itself. This can happen while the component was running, or can be a
side effect of the component terminating.
If the component terminated because it crashed, you can look for a crash report
in `ffx log` that contains the component name in the dump:
```none {:.devsite-disable-click-to-copy}
[33860.645][klog][klog][I] crashsvc: exception received, processing
[33860.645][klog][klog][I] <== fatal : process echo_client.cm[21090] thread initial-thread[21092]
<stack trace follows...>
```
If the source component closed the channel itself, here are some tips to further
troubleshoot the cause:
* Refer to the source component's [logs][doc-logs] for error messages.
* Use `ffx debug fidl` to examine the FIDL connection traffic with
[`fidlcat`][doc-fidlcat] for errors or unexpected behavior.
[core-shard]: /src/sys/core/README.md
[cml-capabilities]: https://fuchsia.dev/reference/cml#capabilities
[cml-children]: https://fuchsia.dev/reference/cml#children
[cml-expose]: https://fuchsia.dev/reference/cml#expose
[cml-offer]: https://fuchsia.dev/reference/cml#offer
[cml-program]: https://fuchsia.dev/reference/cml#program
[cml-use]: https://fuchsia.dev/reference/cml#use
[doc-capabilities]: /docs/concepts/components/v2/capabilities/README.md
[doc-epitaphs]: /docs/reference/fidl/language/wire-format/README.md#epitaph_control_message_ordinal_0xffffffff
[doc-fidlcat]: /docs/development/monitoring/fidlcat/README.md
[doc-lifecycle]: /docs/concepts/components/v2/lifecycle.md
[doc-logs]: /docs/concepts/components/diagnostics/README.md#logs
[doc-manifests]: /docs/concepts/components/v2/component_manifests.md
[doc-packages]: /docs/concepts/packages/package.md
[doc-package-set]: /docs/concepts/packages/package.md#types_of_packages
[doc-protocol]: /docs/concepts/components/v2/capabilities/protocol.md
[doc-realms]: /docs/concepts/components/v2/realms.md
[doc-resolvers]: /docs/concepts/components/v2/capabilities/resolver.md
[doc-runners]: /docs/concepts/components/v2/capabilities/runner.md
[elf-lifecycle]: /docs/concepts/components/v2/elf_runner.md#lifecycle
[example-lifecycle]: /examples/components/lifecycle/
[example-routing]: /examples/components/routing/
[example-routing-failed]: /examples/components/routing_failed/
[fidl-Binder]: https://fuchsia.dev/reference/fidl/fuchsia.component#Binder
[fidl-decl.Child]: https://fuchsia.dev/reference/fidl/fuchsia.component.decl/#Child
[fidl-decl.ChildRef]: https://fuchsia.dev/reference/fidl/fuchsia.component.decl/#ChildRef
[fidl-decl.CollectionRef]: https://fuchsia.dev/reference/fidl/fuchsia.component.decl/#CollectionRef
[fidl-fuchsia.io]: https://fuchsia.dev/reference/fidl/fuchsia.io
[fidl-fuchsia.io.Directory]: https://fuchsia.dev/reference/fidl/fuchsia.io#Directory
[fidl-Lifecycle]: https://fuchsia.dev/reference/fidl/fuchsia.process.lifecycle#Lifecycle
[fidl-Realm]: https://fuchsia.dev/reference/fidl/fuchsia.component#Realm
[fidl-Realm.CreateChild]: https://fuchsia.dev/reference/fidl/fuchsia.component#Realm.CreateChild
[fidl-Realm.DestroyChild]: https://fuchsia.dev/reference/fidl/fuchsia.component#Realm.DestroyChild
[fidl-Realm.OpenExposedDir]: https://fuchsia.dev/reference/fidl/fuchsia.component#Realm.OpenExposedDir
[glossary.capability]: /docs/glossary/README.md#capability
[glossary.component-collection]: /docs/glossary/README.md#component-collection
[glossary.component-declaration]: /docs/glossary/README.md#component-declaration
[glossary.component-instance]: /docs/glossary/README.md#component-instance
[glossary.component-instance-tree]: /docs/glossary/README.md#component-instance-tree
[glossary.component-manifest]: /docs/glossary/README.md#component-manifest
[glossary.component-topology]: /docs/glossary/README.md#component-topology
[glossary.exposed-directory]: /docs/glossary/README.md#exposed-directory
[glossary.namespace]: /docs/glossary/README.md#namespace
[glossary.outgoing-directory]: /docs/glossary/README.md#outgoing-directory
[src-security-policy]: /src/security/policy/component_manager_policy.json5
[static-analyzer]: /docs/development/components/build.md#troubleshoot-build-analyzer