| # Connect components |
| |
| This document demonstrates how to connect components 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 executable makes discoverable any capabilities that it provides |
| through this directory. |
| * At runtime, the |
| [component instance tree][glossary.component-instance-tree] connects individual |
| [component instances][glossary.component-instance] together in a hierarchy of |
| parent and child relationships. 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 {#capabilities} |
| |
| Note: For a complete example using routed capabilities, see |
| [`//examples/components/routing`][example-routing]. |
| |
| Components interact with each other through their capabilities. Capabilities implemented |
| in a component need to be declared in that component's manifest and routed through its |
| parent/child components. Other components that use that capability also need to declare |
| their use in their manifests. This capability routing describes which component should act |
| as the provider for any given client. Once the proper components are identified, the |
| component manager initiates connections between components. |
| |
| ### Provide a capability implementation {#provide-capability} |
| |
| Components that implement a capability must declare the implementation 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 provider component provides an implementation of the capability by serving |
| it through the 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 request capabilities 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 connects to the capability handles populated in its namespace |
| using the [fuchsia.io][fidl-fuchsia.io] protocol. The Fuchsia component library works |
| with the generated FIDL bindings to wrap these handles and 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" %} |
| ``` |
| |
| #### 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 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_UNAVAILABLE` 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 it's 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. |
| |
| ``` |
| 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": |
| |
| ``` |
| 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: Required protocol `fuchsia.appmgr.Startup` was not |
| available for target component `/startup`: |
| failed to resolve "fuchsia-pkg://fuchsia.com/your_component#meta/your_component.cm": |
| package not found: remote resolver responded with PackageNotFound |
| ``` |
| |
| 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 (including |
| 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. |
| If you believe you need this option, please reach out to the |
| [Component Framework team][cf-dev-list]. |
| |
| 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", |
| ], |
| }, |
| }, |
| } |
| ``` |
| |
| ## Troubleshooting {#troubleshooting} |
| |
| This section contains common issues you may encounter trying to `use` and connect to |
| capabilities from your component. |
| |
| When component connections fail, 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); |
| }); |
| ``` |
| |
| ### 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: |
| |
| * 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] Required protocol |
| `fidl.examples.routing.echo.Echo` was not available for target component |
| `/core/ffx-laboratory:echo_realm/echo_client`: |
| A `use from parent` declaration was found at `/core/ffx-laboratory:echo_realm/echo_client` |
| for `fidl.examples.routing.echo.Echo`, but no matching `offer` declaration was found in the parent |
| ``` |
| |
| * Check for an [epitaph on the closed channel](#troubleshooting). |
| Normally, the epitaph set for a routing failure is `ZX_ERR_UNAVAILABLE`: |
| |
| ```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: UNAVAILABLE" |
| ``` |
| |
| For a self-contained example of failed capability routing, see |
| [`//examples/components/routing_failed`][example-routing-failed]. |
| |
| ### 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 (anonymous) 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. |
| |
| [cf-dev-list]: https://groups.google.com/a/fuchsia.dev/g/component-framework-dev |
| [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/logs/README.md |
| [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-runners]: /docs/concepts/components/v2/capabilities/runners.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 |