use anyhow::format_err;
use async_trait::async_trait;
use cm_rust::ComponentDecl;
use cm_types::{Name, Url};
use errors::ModelError;
use futures::channel::oneshot;
use futures::lock::Mutex;
use moniker::{ExtendedMoniker, Moniker};
use sandbox::{Connector, Receiver, WeakInstanceToken};
use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex as StdMutex, Weak};
use tracing::warn;
use {
fidl_fuchsia_component as fcomponent, fidl_fuchsia_diagnostics_types as fdiagnostics,
fidl_fuchsia_io as fio,
};
pub trait HasEventType {
fn event_type(&self) -> EventType;
}
#[async_trait]
pub trait TransferEvent {
async fn transfer(&self) -> Self;
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum EventType {
CapabilityRequested,
Destroyed,
Resolved,
Started,
Stopped,
DebugStarted,
Unresolved,
}
impl EventType {
fn as_str(&self) -> &str {
match self {
EventType::CapabilityRequested => "capability_requested",
EventType::Destroyed => "destroyed",
EventType::Resolved => "resolved",
EventType::Started => "started",
EventType::Stopped => "stopped",
EventType::DebugStarted => "debug_started",
EventType::Unresolved => "unresolved",
}
}
pub fn values() -> Vec<EventType> {
vec![
EventType::CapabilityRequested,
EventType::Destroyed,
EventType::Resolved,
EventType::Started,
EventType::Stopped,
EventType::DebugStarted,
EventType::Unresolved,
]
}
}
impl From<EventType> for Name {
fn from(event_type: EventType) -> Name {
event_type.as_str().parse().unwrap()
}
}
impl fmt::Display for EventType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl TryFrom<String> for EventType {
type Error = anyhow::Error;
fn try_from(string: String) -> Result<EventType, Self::Error> {
for value in EventType::values() {
if value.as_str() == string {
return Ok(value);
}
}
Err(format_err!("invalid string for event type: {:?}", string))
}
}
impl HasEventType for EventPayload {
fn event_type(&self) -> EventType {
match self {
EventPayload::CapabilityRequested { .. } => EventType::CapabilityRequested,
EventPayload::Destroyed => EventType::Destroyed,
EventPayload::Resolved { .. } => EventType::Resolved,
EventPayload::Started { .. } => EventType::Started,
EventPayload::Stopped { .. } => EventType::Stopped,
EventPayload::DebugStarted { .. } => EventType::DebugStarted,
EventPayload::Unresolved => EventType::Unresolved,
}
}
}
impl From<fcomponent::EventType> for EventType {
fn from(fidl_event_type: fcomponent::EventType) -> Self {
match fidl_event_type {
fcomponent::EventType::CapabilityRequested => EventType::CapabilityRequested,
fcomponent::EventType::DirectoryReady => unreachable!("This isn't used anymore"),
fcomponent::EventType::Discovered => unreachable!("This isn't used anymore"),
fcomponent::EventType::Destroyed => EventType::Destroyed,
fcomponent::EventType::Resolved => EventType::Resolved,
fcomponent::EventType::Started => EventType::Started,
fcomponent::EventType::Stopped => EventType::Stopped,
fcomponent::EventType::DebugStarted => EventType::DebugStarted,
fcomponent::EventType::Unresolved => EventType::Unresolved,
}
}
}
impl TryInto<fcomponent::EventType> for EventType {
type Error = anyhow::Error;
fn try_into(self) -> Result<fcomponent::EventType, anyhow::Error> {
match self {
EventType::CapabilityRequested => Ok(fcomponent::EventType::CapabilityRequested),
EventType::Destroyed => Ok(fcomponent::EventType::Destroyed),
EventType::Resolved => Ok(fcomponent::EventType::Resolved),
EventType::Started => Ok(fcomponent::EventType::Started),
EventType::Stopped => Ok(fcomponent::EventType::Stopped),
EventType::DebugStarted => Ok(fcomponent::EventType::DebugStarted),
EventType::Unresolved => Ok(fcomponent::EventType::Unresolved),
}
}
}
#[async_trait]
pub trait Hook: Send + Sync {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError>;
}
#[derive(Clone)]
pub struct HooksRegistration {
events: Vec<EventType>,
callback: Weak<dyn Hook>,
}
impl HooksRegistration {
pub fn new(
_name: &'static str,
events: Vec<EventType>,
callback: Weak<dyn Hook>,
) -> HooksRegistration {
Self { events, callback }
}
}
#[derive(Clone)]
pub struct CapabilityReceiver {
inner: Arc<StdMutex<Option<Receiver>>>,
}
impl CapabilityReceiver {
pub fn new() -> (Self, Connector) {
let (receiver, sender) = Connector::new();
let inner = Arc::new(StdMutex::new(Some(receiver)));
(Self { inner }, sender)
}
pub fn take(&self) -> Option<Receiver> {
self.inner.lock().unwrap().take()
}
pub fn is_taken(&self) -> bool {
self.inner.lock().unwrap().is_none()
}
}
#[async_trait]
impl TransferEvent for CapabilityReceiver {
async fn transfer(&self) -> Self {
let receiver = self.take();
let inner = Arc::new(StdMutex::new(receiver));
Self { inner }
}
}
#[derive(Clone)]
pub enum EventPayload {
CapabilityRequested {
source_moniker: Moniker,
name: String,
receiver: CapabilityReceiver,
},
Destroyed,
Resolved {
component: WeakInstanceToken,
decl: ComponentDecl,
},
Unresolved,
Started {
runtime: RuntimeInfo,
component_decl: ComponentDecl,
},
Stopped {
status: zx::Status,
exit_code: Option<i64>,
stop_time: zx::BootInstant,
stop_time_monotonic: zx::MonotonicInstant,
execution_duration: zx::MonotonicDuration,
requested_escrow: bool,
},
DebugStarted {
runtime_dir: Option<fio::DirectoryProxy>,
break_on_start: Arc<zx::EventPair>,
},
}
#[derive(Clone)]
pub struct RuntimeInfo {
pub diagnostics_receiver:
Arc<Mutex<Option<oneshot::Receiver<fdiagnostics::ComponentDiagnostics>>>>,
pub start_time: zx::BootInstant,
pub start_time_monotonic: zx::MonotonicInstant,
}
impl RuntimeInfo {
pub fn new(
timestamp: zx::BootInstant,
timestamp_monotonic: zx::MonotonicInstant,
diagnostics_receiver: oneshot::Receiver<fdiagnostics::ComponentDiagnostics>,
) -> Self {
let diagnostics_receiver = Arc::new(Mutex::new(Some(diagnostics_receiver)));
Self {
diagnostics_receiver,
start_time: timestamp,
start_time_monotonic: timestamp_monotonic,
}
}
}
impl fmt::Debug for EventPayload {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut formatter = fmt.debug_struct("EventPayload");
formatter.field("type", &self.event_type());
match self {
EventPayload::CapabilityRequested { name, .. } => {
formatter.field("name", &name).finish()
}
EventPayload::Started { component_decl, .. } => {
formatter.field("component_decl", &component_decl).finish()
}
EventPayload::Resolved { component: _, decl, .. } => {
formatter.field("decl", decl).finish()
}
EventPayload::Stopped { status, exit_code, .. } => {
formatter.field("status", status).field("exit_code", exit_code).finish()
}
EventPayload::Unresolved
| EventPayload::Destroyed
| EventPayload::DebugStarted { .. } => formatter.finish(),
}
}
}
#[derive(Clone, Debug)]
pub struct Event {
pub target_moniker: ExtendedMoniker,
pub component_url: Url,
pub payload: EventPayload,
pub timestamp: zx::BootInstant,
}
impl Event {
pub fn new_builtin(payload: EventPayload) -> Self {
Self {
target_moniker: ExtendedMoniker::ComponentManager,
component_url: "file:///bin/component_manager".parse().unwrap(),
payload,
timestamp: zx::BootInstant::get(),
}
}
}
#[async_trait]
impl TransferEvent for EventPayload {
async fn transfer(&self) -> Self {
match self {
EventPayload::CapabilityRequested { source_moniker, name, receiver } => {
EventPayload::CapabilityRequested {
source_moniker: source_moniker.clone(),
name: name.to_string(),
receiver: receiver.transfer().await,
}
}
result => result.clone(),
}
}
}
impl HasEventType for Event {
fn event_type(&self) -> EventType {
self.payload.event_type()
}
}
#[async_trait]
impl TransferEvent for Event {
async fn transfer(&self) -> Self {
Self {
target_moniker: self.target_moniker.clone(),
component_url: self.component_url.clone(),
payload: self.payload.transfer().await,
timestamp: self.timestamp,
}
}
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let payload = match &self.payload {
EventPayload::CapabilityRequested { source_moniker, name, .. } => {
format!("requested '{}' from '{}'", name.to_string(), source_moniker)
}
EventPayload::Stopped { status, .. } => {
format!("with status: {}", status.to_string())
}
EventPayload::Destroyed { .. }
| EventPayload::Resolved { .. }
| EventPayload::DebugStarted { .. }
| EventPayload::Started { .. }
| EventPayload::Unresolved => "".to_string(),
};
write!(f, "[{}] '{}' {}", self.event_type().to_string(), self.target_moniker, payload)
}
}
pub struct Hooks {
hooks_map: Mutex<HashMap<EventType, Vec<Weak<dyn Hook>>>>,
}
impl Hooks {
pub fn new() -> Self {
Self { hooks_map: Mutex::new(HashMap::new()) }
}
pub async fn install(&self, hooks: Vec<HooksRegistration>) {
let mut hooks_map = self.hooks_map.lock().await;
for hook in hooks {
for event in hook.events {
let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
existing_hooks.push(hook.callback.clone());
}
}
}
pub async fn install_front_for_test(&self, hooks: Vec<HooksRegistration>) {
let mut hooks_map = self.hooks_map.lock().await;
for hook in hooks {
for event in hook.events {
let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
existing_hooks.insert(0, hook.callback.clone());
}
}
}
pub async fn dispatch(&self, event: &Event) {
let strong_hooks = {
let mut hooks_map = self.hooks_map.lock().await;
if let Some(hooks) = hooks_map.get_mut(&event.event_type()) {
let mut strong_hooks = vec![];
hooks.retain(|hook| {
if let Some(hook) = hook.upgrade() {
strong_hooks.push(hook);
true
} else {
false
}
});
strong_hooks
} else {
vec![]
}
};
for hook in strong_hooks {
if let Err(err) = hook.on(event).await {
warn!(%err, %event, "Hook produced error for event");
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[fuchsia::test]
async fn capability_requested_transfer() {
let (receiver, _sender) = CapabilityReceiver::new();
let event = Event {
target_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
component_url: "fuchsia-pkg://root".parse().unwrap(),
payload: EventPayload::CapabilityRequested {
source_moniker: Moniker::root(),
name: "foo".to_string(),
receiver,
},
timestamp: zx::BootInstant::get(),
};
let transferred_event = event.transfer().await;
match transferred_event.payload {
EventPayload::CapabilityRequested { receiver, .. } => {
assert!(!receiver.is_taken());
}
_ => panic!("Event type unexpected"),
}
match &event.payload {
EventPayload::CapabilityRequested { receiver, .. } => {
assert!(receiver.is_taken());
}
_ => panic!("Event type unexpected"),
}
let second_transferred_event = event.transfer().await;
match &second_transferred_event.payload {
EventPayload::CapabilityRequested { receiver, .. } => {
assert!(receiver.is_taken());
}
_ => panic!("Event type unexpected"),
}
}
}