blob: dd2168ee217e8fab4fab00bc56beb466a51d397e [file] [log] [blame]
// Copyright 2023 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.cti
use {
crate::model::{
actions::StopAction,
component::{IncomingCapabilities, StartReason, WeakComponentInstance},
},
fidl::endpoints::RequestStream,
fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_sandbox as fsandbox,
fuchsia_async as fasync, fuchsia_zircon as zx,
futures::prelude::*,
sandbox::Capability,
tracing::{error, warn},
};
pub async fn run_controller(
weak_component_instance: WeakComponentInstance,
stream: fcomponent::ControllerRequestStream,
) {
if let Err(err) = serve_controller(weak_component_instance, stream).await {
warn!(%err, "failed to serve controller");
}
}
pub async fn serve_controller(
weak_component_instance: WeakComponentInstance,
mut stream: fcomponent::ControllerRequestStream,
) -> Result<(), fidl::Error> {
let mut tasks = fasync::TaskGroup::new();
while let Some(request) = stream.try_next().await? {
match request {
fcomponent::ControllerRequest::Start { mut args, execution_controller, responder } => {
let component = weak_component_instance.upgrade();
let Ok(component) = component else {
responder.send(Err(fcomponent::Error::InstanceNotFound))?;
continue;
};
if component.is_started().await {
responder.send(Err(fcomponent::Error::InstanceAlreadyStarted))?;
continue;
}
let execution_controller_stream = execution_controller.into_stream()?;
let control_handle = execution_controller_stream.control_handle();
let execution_controller = ExecutionControllerTask {
_task: fasync::Task::spawn(execution_controller_task(
weak_component_instance.clone(),
execution_controller_stream,
)),
control_handle,
stop_payload: None,
};
let incoming = {
let numbered_handles = args.numbered_handles.take().unwrap_or_default();
let Ok(namespace): Result<namespace::Namespace, _> =
args.namespace_entries.take().unwrap_or_default().try_into()
else {
responder.send(Err(fcomponent::Error::InvalidArguments))?;
continue;
};
let dict = if let Some(dict_client_end) = args.dictionary {
let fidl_capability = fsandbox::Capability::Dictionary(dict_client_end);
let Ok(any) = Capability::try_from(fidl_capability) else {
responder.send(Err(fcomponent::Error::InvalidArguments))?;
continue;
};
let dict = match any {
Capability::Dictionary(d) => d,
_ => {
responder.send(Err(fcomponent::Error::InvalidArguments))?;
continue;
}
};
Some(dict)
} else {
None
};
IncomingCapabilities {
numbered_handles,
additional_namespace_entries: namespace.into(),
dict,
}
};
if let Err(err) = component
.start(&StartReason::Controller, Some(execution_controller), incoming)
.await
{
warn!(%err, "failed to start component");
responder.send(Err(err.into()))?;
continue;
}
responder.send(Ok(()))?;
}
fcomponent::ControllerRequest::IsStarted { responder } => {
let component = weak_component_instance.upgrade();
if component.is_err() {
responder.send(Err(fcomponent::Error::InstanceNotFound))?;
continue;
}
let component = component.unwrap();
responder.send(Ok(component.is_started().await))?;
}
fcomponent::ControllerRequest::GetExposedDictionary { dictionary, responder } => {
let res = async {
let component = weak_component_instance
.upgrade()
.map_err(|_| fcomponent::Error::InstanceNotFound)?;
let resolved = component
.lock_resolved_state()
.await
.map_err(|_| fcomponent::Error::InstanceCannotResolve)?;
let mut exposed_dict = resolved.make_exposed_dict().await;
tasks.spawn(async move {
if let Err(err) =
exposed_dict.serve_dict(dictionary.into_stream().unwrap()).await
{
warn!(%err, "failed to serve dict");
}
});
Ok(())
}
.await;
responder.send(res)?;
}
fcomponent::ControllerRequest::_UnknownMethod { ordinal, .. } => {
warn!(%ordinal, "fuchsia.component/Controller received unknown method");
}
}
}
Ok(())
}
async fn execution_controller_task(
weak_component_instance: WeakComponentInstance,
mut stream: fcomponent::ExecutionControllerRequestStream,
) {
while let Ok(Some(request)) = stream.try_next().await {
match request {
fcomponent::ExecutionControllerRequest::Stop { control_handle: _ } => {
let component = weak_component_instance.upgrade();
if component.is_err() {
return;
}
let component = component.unwrap();
let mut action_set = component.lock_actions().await;
let _ = action_set.register_no_wait(&component, StopAction::new(false)).await;
}
fcomponent::ExecutionControllerRequest::_UnknownMethod { ordinal, .. } => {
warn!(%ordinal, "fuchsia.component/ExecutionController received unknown method");
}
}
}
}
pub struct ExecutionControllerTask {
_task: fasync::Task<()>,
control_handle: fcomponent::ExecutionControllerControlHandle,
stop_payload: Option<zx::Status>,
}
impl Drop for ExecutionControllerTask {
fn drop(&mut self) {
match self.stop_payload.as_ref() {
Some(status) => {
// There's not much we can do if the other end has closed their channel
let _ = self.control_handle.send_on_stop(&fcomponent::StoppedPayload {
status: Some(status.into_raw()),
..Default::default()
});
}
None => {
// TODO(https://fxbug.dev/42081036): stop_payload is not when system is shutting down
error!("stop_payload was not set before the ExecutionControllerTask was dropped");
}
}
}
}
impl ExecutionControllerTask {
pub fn set_stop_status(&mut self, status: zx::Status) {
self.stop_payload = Some(status);
}
}