| use crate::handler::base::Error; |
| |
| use fuchsia_syslog::fx_log_warn; |
| |
| // Copyright 2020 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. |
| |
| /// This macro generates a mod containing the logic to process a FIDL stream for the |
| /// fuchsia.settings namespace. |
| /// Callers can spawn a handler task by invoking fidl_io::spawn. |
| /// Macro usages specify the interface's base name (prefix for all generated |
| /// classes), along with a repeated set of the following: |
| /// - `Switchboard` setting type. |
| /// - FIDL setting type. |
| /// - The responder type for the `HangingGetHandler`. |
| /// - A type of a key for change functions if custom change functions are used. |
| /// - If change functions aren't used, the `change_func_key` is ignored, |
| /// so can be anything. |
| /// TODO(fxb/68167): Restructure this code to avoid needing to specify |
| /// unneeded parameters. |
| /// - The handler function for requests. |
| #[macro_export] |
| macro_rules! fidl_process_full { |
| ($interface:ident $(,$setting_type:expr, $fidl_settings:ty, |
| $fidl_responder:ty, $change_func_key:ty, $handle_func:ident)+$(,)*) => { |
| type HandleResult<'a> = ::futures::future::LocalBoxFuture< |
| 'a, |
| Result<Option<paste::paste!{[<$interface Request>]}>, anyhow::Error>, |
| >; |
| |
| pub(crate) mod fidl_io { |
| paste::paste!{ |
| use fidl_fuchsia_settings::{[<$interface Marker>], [<$interface RequestStream>]}; |
| } |
| use super::*; |
| use $crate::fidl_processor::processor::SettingsFidlProcessor; |
| use $crate::service; |
| use $crate::message::base::MessengerType; |
| use ::fuchsia_async as fasync; |
| use ::futures::FutureExt; |
| |
| pub(crate) fn spawn ( |
| delegate: service::message::Delegate, |
| stream: paste::paste!{[<$interface RequestStream>]} |
| ) { |
| fasync::Task::local(async move { |
| let service_messenger = delegate |
| .create(MessengerType::Unbound) |
| .await.expect("service messenger should be created") |
| .0; |
| |
| let mut processor = |
| SettingsFidlProcessor::<paste::paste!{[<$interface Marker>]}>::new( |
| stream, service_messenger, |
| ) |
| .await; |
| $( |
| processor |
| .register::<$fidl_settings, $fidl_responder, $change_func_key>( |
| $setting_type, |
| Box::new(move |context, req| -> HandleResult<'_> { |
| async move { $handle_func(context, req).await }.boxed_local() |
| }), |
| ) |
| .await; |
| )* |
| processor.process().await; |
| }).detach(); |
| } |
| } |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! fidl_process { |
| // Generates a fidl_io mod with a spawn for the given fidl interface, |
| // setting type, and handler function. Additional handlers can be specified |
| // by providing the internal setting type, fidl setting type, watch |
| // responder, and handle function. |
| ($interface:ident, $setting_type:expr, $handle_func:ident |
| $(,$item_setting_type:expr, $fidl_settings:ty, $fidl_responder:ty, |
| $item_handle_func:ident)*$(,)*) => { |
| paste::paste! { |
| $crate::fidl_process_full!( |
| $interface, |
| $setting_type, |
| [<$interface Settings>], |
| [<$interface WatchResponder>], |
| String, |
| $handle_func |
| $(,$item_setting_type, $fidl_settings, $fidl_responder, String, $item_handle_func)* |
| ); |
| } |
| }; |
| |
| // Generates a fidl_io mod with a spawn for the given fidl interface, |
| // setting type, and handler function. Additional handlers can be specified |
| // by providing the responder type and handle function. |
| ($interface:ident, $setting_type:expr, $handle_func:ident |
| $(, $fidl_responder:ty, $item_handle_func:ident)*$(,)*) => { |
| paste::paste! { |
| $crate::fidl_process_full!( |
| $interface, |
| $setting_type, |
| [<$interface Settings>], |
| [<$interface WatchResponder>], |
| String, |
| $handle_func |
| $(, $setting_type, |
| [<$interface Settings>], |
| $fidl_responder, |
| String, |
| $item_handle_func)* |
| ); |
| } |
| }; |
| |
| // Generates a fidl_io mod with a spawn for the given fidl interface, |
| // setting type, fidl setting type and handler function. To be used when the |
| // fidl interface and fidl setting type differ in name. |
| ($interface:ident, $setting_type:expr, $fidl_settings:ident, |
| $handle_func:ident) => { |
| paste::paste! { |
| $crate::fidl_process_full!( |
| $interface, |
| $setting_type, |
| $fidl_settings, |
| [<$interface WatchResponder>], |
| String, |
| $handle_func |
| ); |
| } |
| }; |
| } |
| |
| // Fully custom named inputs for when there are multiple simultaneous interfaces |
| // that do not have one specific pattern to conform to. |
| // TODO(fxbug.dev/65686): Remove once clients are migrated to input2. |
| #[macro_export] |
| macro_rules! fidl_process_custom { |
| ($interface:ident, $setting_type:expr, $fidl_responder:ty, $fidl_settings:ident, $handle_func:ident |
| $(, $item_setting_type:expr, $item_fidl_responder:ty, $item_fidl_settings:ident, $item_handle_func:ident)*$(,)*) => { |
| paste::paste! { |
| $crate::fidl_process_full!( |
| $interface, |
| $setting_type, |
| $fidl_settings, |
| $fidl_responder, |
| String, |
| $handle_func |
| $(, $item_setting_type, |
| $item_fidl_settings, |
| $item_fidl_responder, |
| String, |
| $item_handle_func)* |
| ); |
| } |
| }; |
| } |
| |
| pub(crate) fn convert_to_epitaph(error: &anyhow::Error) -> fuchsia_zircon::Status { |
| match error.root_cause().downcast_ref::<Error>() { |
| Some(Error::UnhandledType(_)) => fuchsia_zircon::Status::UNAVAILABLE, |
| _ => fuchsia_zircon::Status::INTERNAL, |
| } |
| } |
| |
| /// Shuts down the given fidl `responder` using a zircon epitaph generated from |
| /// the given `error`. |
| #[macro_export] |
| macro_rules! shutdown_responder_with_error { |
| ($responder:expr, $error:ident) => { |
| // Extra pair of braces is needed to limit the scope of the import. |
| { |
| use ::fidl::prelude::*; |
| $responder |
| .control_handle() |
| .shutdown_with_epitaph(crate::fidl_common::convert_to_epitaph($error)) |
| } |
| }; |
| } |
| |
| /// Implements the Sender trait for the given FIDL responder(s) that send typed data. |
| #[macro_export] |
| macro_rules! fidl_hanging_get_responder { |
| ($marker_type:ty $(, $setting_type:ty, $responder_type:ty)+$(,)*) => { |
| |
| $(impl $crate::hanging_get_handler::Sender<$setting_type> for $responder_type { |
| fn send_response(self, data: $setting_type) { |
| use $crate::fidl_common::FidlResponseErrorLogger; |
| |
| self.send(data).log_fidl_response_error( |
| <$marker_type as ::fidl::endpoints::ProtocolMarker>::DEBUG_NAME); |
| } |
| |
| fn on_error(self, error: &anyhow::Error) { |
| ::fuchsia_syslog::fx_log_err!( |
| "error occurred watching for service: {:?}. Error is: {:?}", |
| <$marker_type as ::fidl::endpoints::ProtocolMarker>::DEBUG_NAME, |
| error |
| ); |
| crate::shutdown_responder_with_error!(self, error); |
| } |
| })+ |
| }; |
| } |
| |
| /// Implements the Sender trait for the given FIDL responder(s) that send a result type. |
| #[macro_export] |
| macro_rules! fidl_result_sender_for_responder { |
| ($marker_type:ty $(, $result_type:ty, $responder_type:ty)+$(,)*) => { |
| $(impl $crate::hanging_get_handler::Sender<$result_type> for $responder_type { |
| fn send_response(self, mut result: $result_type) { |
| use $crate::fidl_common::FidlResponseErrorLogger; |
| |
| self.send(&mut result).log_fidl_response_error( |
| <$marker_type as ::fidl::endpoints::ProtocolMarker>::DEBUG_NAME); |
| } |
| |
| fn on_error(self, error:&anyhow::Error) { |
| ::fuchsia_syslog::fx_log_err!( |
| "error occurred watching for service: {:?}. Error is: {:?}", |
| <$marker_type as ::fidl::endpoints::ProtocolMarker>::DEBUG_NAME, |
| error |
| ); |
| crate::shutdown_responder_with_error!(self, error); |
| } |
| })+ |
| }; |
| } |
| |
| /// Custom trait used to handle results from responding to FIDL calls. |
| pub trait FidlResponseErrorLogger { |
| fn log_fidl_response_error(&self, client_name: &str); |
| } |
| |
| /// In order to not crash when a client dies, logs but doesn't crash for the specific case of |
| /// being unable to write to the client. Crashes if other types of errors occur. |
| impl FidlResponseErrorLogger for Result<(), fidl::Error> { |
| fn log_fidl_response_error(&self, client_name: &str) { |
| if let Some(error) = self.as_ref().err() { |
| match error { |
| fidl::Error::ServerResponseWrite(_) => { |
| fx_log_warn!("Failed to respond to client {:?} : {:?}", client_name, error); |
| } |
| _ => { |
| panic!( |
| "Unexpected client response error from client {:?} : {:?}", |
| client_name, error |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use fuchsia_zircon as zx; |
| |
| use super::*; |
| |
| // Should succeed either when responding was successful or there was an error on the client |
| // side. |
| #[test] |
| fn test_error_logger_succeeds() { |
| let result = Err(fidl::Error::ServerResponseWrite(zx::Status::PEER_CLOSED)); |
| result.log_fidl_response_error(""); |
| |
| let result = Ok(()); |
| result.log_fidl_response_error(""); |
| } |
| |
| // Should fail at all other times. |
| #[should_panic] |
| #[test] |
| fn test_error_logger_fails() { |
| let result = Err(fidl::Error::ServerRequestRead(zx::Status::PEER_CLOSED)); |
| result.log_fidl_response_error(""); |
| } |
| } |