fdf_component/
context.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{Incoming, Node};
6use fuchsia_component::server::{ServiceFs, ServiceObjTrait};
7use fuchsia_component_config::Config;
8use log::error;
9use namespace::Namespace;
10use zx::Status;
11
12use fdf::DispatcherRef;
13use fidl_fuchsia_driver_framework::DriverStartArgs;
14
15/// The context arguments passed to the driver in its start arguments.
16pub struct DriverContext {
17    /// A reference to the root [`fdf::Dispatcher`] for this driver.
18    pub root_dispatcher: DispatcherRef<'static>,
19    /// The original [`DriverStartArgs`] passed in as start arguments, minus any parts that were
20    /// used to construct other elements of [`Self`].
21    pub start_args: DriverStartArgs,
22    /// The incoming namespace constructed from [`DriverStartArgs::incoming`]. Since producing this
23    /// consumes the incoming namespace from [`Self::start_args`], that will always be [`None`].
24    pub incoming: Incoming,
25    #[doc(hidden)]
26    _private: (),
27}
28
29impl DriverContext {
30    /// Binds the node proxy client end from the start args into a [`NodeProxy`] that can be used
31    /// to add child nodes. Dropping this proxy will result in the node being removed and the
32    /// driver starting shutdown, so it should be bound and stored in your driver object in its
33    /// [`crate::Driver::start`] method.
34    ///
35    /// After calling this, [`DriverStartArgs::node`] in [`Self::start_args`] will be `None`.
36    ///
37    /// Returns [`Status::INVALID_ARGS`] if the node client end is not present in the start
38    /// arguments.
39    pub fn take_node(&mut self) -> Result<Node, Status> {
40        let node_client = self.start_args.node.take().ok_or(Status::INVALID_ARGS)?;
41        Ok(Node::from(node_client.into_proxy()))
42    }
43
44    /// Returns the component config.
45    ///
46    /// After calling this, [`DriverStartArgs::config`] in [`Self::start_args`] will be `None`.
47    ///
48    /// Returns [`Status::INVALID_ARGS`] if the config is not present in the start arguments.
49    pub fn take_config<C: Config>(&mut self) -> Result<C, Status> {
50        let vmo = self.start_args.config.take().ok_or(Status::INVALID_ARGS)?;
51        Ok(Config::from_vmo(&vmo).expect("Config VMO handle must be valid."))
52    }
53
54    /// Serves the given [`ServiceFs`] on the node's outgoing directory. This can only be called
55    /// once, and after this the [`DriverStartArgs::outgoing_dir`] member will be [`None`].
56    ///
57    /// Logs an error and returns [`Status::INVALID_ARGS`] if the outgoing directory server end is
58    /// not present in the start arguments, or [`Status::INTERNAL`] if serving the connection
59    /// failed.
60    pub fn serve_outgoing<O: ServiceObjTrait>(
61        &mut self,
62        outgoing_fs: &mut ServiceFs<O>,
63    ) -> Result<(), Status> {
64        let Some(outgoing_dir) = self.start_args.outgoing_dir.take() else {
65            error!("Tried to serve on outgoing directory but it wasn't available");
66            return Err(Status::INVALID_ARGS);
67        };
68        outgoing_fs.serve_connection(outgoing_dir).map_err(|err| {
69            error!("Failed to serve outgoing directory: {err}");
70            Status::INTERNAL
71        })?;
72
73        Ok(())
74    }
75
76    pub(crate) fn new(
77        root_dispatcher: DispatcherRef<'static>,
78        mut start_args: DriverStartArgs,
79    ) -> Result<Self, Status> {
80        let incoming_namespace: Namespace = start_args
81            .incoming
82            .take()
83            .unwrap_or_else(|| vec![])
84            .try_into()
85            .map_err(|_| Status::INVALID_ARGS)?;
86        let incoming = incoming_namespace.try_into().map_err(|_| Status::INVALID_ARGS)?;
87        Ok(DriverContext { root_dispatcher, start_args, incoming, _private: () })
88    }
89
90    pub(crate) fn start_logging(&self, driver_name: &str) -> Result<(), Status> {
91        let log_proxy = match self.incoming.connect_protocol() {
92            Ok(log_proxy) => log_proxy,
93            Err(err) => {
94                eprintln!("Error connecting to log sink proxy at driver startup: {err}. Continuing without logging.");
95                return Ok(());
96            }
97        };
98
99        if let Err(e) = diagnostics_log::initialize(
100            diagnostics_log::PublishOptions::default()
101                .use_log_sink(log_proxy)
102                .tags(&["driver", driver_name]),
103        ) {
104            eprintln!("Error initializing logging at driver startup: {e}");
105        }
106        Ok(())
107    }
108}