Skip to main content

hooks/
lib.rs

1// Copyright 2019 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 anyhow::format_err;
6use async_trait::async_trait;
7use cm_types::{Name, Url};
8use errors::ModelError;
9use fidl_fuchsia_component as fcomponent;
10use fidl_fuchsia_component_runner as fcrunner;
11use fidl_fuchsia_io as fio;
12use fuchsia_sync::Mutex;
13use futures::channel::oneshot;
14use log::warn;
15use moniker::{ExtendedMoniker, Moniker};
16use runtime_capabilities::{Connector, Receiver, WeakInstanceToken};
17use std::collections::HashMap;
18use std::fmt;
19use std::sync::{Arc, Weak};
20
21pub trait HasEventType {
22    fn event_type(&self) -> EventType;
23}
24
25/// Transfers any move-only state out of self into a new event that is otherwise
26/// a clone.
27pub trait TransferEvent: Send + Sync {
28    fn transfer(&self) -> impl std::future::Future<Output = Self> + Send;
29}
30
31#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
32pub enum EventType {
33    /// After a CapabilityProvider has been selected, the CapabilityRequested event is dispatched
34    /// with the ServerEnd of the channel for the capability.
35    CapabilityRequested,
36    /// Destruction of an instance has begun. The instance may/may not be stopped by this point.
37    /// The instance still exists in the parent's realm but will soon be removed.
38    Destroyed,
39    /// An instance's declaration was resolved successfully for the first time.
40    Resolved,
41    /// An instance is about to be started.
42    Started,
43    /// An instance was stopped successfully.
44    /// This event must occur before Destroyed.
45    Stopped,
46    /// Similar to the Started event, except the payload will carry an eventpair
47    /// that the subscriber could use to defer the launch of the component.
48    DebugStarted,
49    /// A component instance was unresolved.
50    Unresolved,
51}
52
53impl EventType {
54    pub fn as_str(&self) -> &str {
55        match self {
56            EventType::CapabilityRequested => "capability_requested",
57            EventType::Destroyed => "destroyed",
58            EventType::Resolved => "resolved",
59            EventType::Started => "started",
60            EventType::Stopped => "stopped",
61            EventType::DebugStarted => "debug_started",
62            EventType::Unresolved => "unresolved",
63        }
64    }
65
66    /// Returns all available event types.
67    pub fn values() -> Vec<EventType> {
68        vec![
69            EventType::CapabilityRequested,
70            EventType::Destroyed,
71            EventType::Resolved,
72            EventType::Started,
73            EventType::Stopped,
74            EventType::DebugStarted,
75            EventType::Unresolved,
76        ]
77    }
78}
79
80impl From<EventType> for Name {
81    fn from(event_type: EventType) -> Name {
82        event_type.as_str().parse().unwrap()
83    }
84}
85
86impl fmt::Display for EventType {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        write!(f, "{}", self.as_str())
89    }
90}
91
92impl TryFrom<String> for EventType {
93    type Error = anyhow::Error;
94    fn try_from(string: String) -> Result<EventType, Self::Error> {
95        for value in EventType::values() {
96            if value.as_str() == string {
97                return Ok(value);
98            }
99        }
100        Err(format_err!("invalid string for event type: {:?}", string))
101    }
102}
103
104impl HasEventType for EventPayload {
105    fn event_type(&self) -> EventType {
106        match self {
107            EventPayload::CapabilityRequested { .. } => EventType::CapabilityRequested,
108            EventPayload::Destroyed => EventType::Destroyed,
109            EventPayload::Resolved { .. } => EventType::Resolved,
110            EventPayload::Started { .. } => EventType::Started,
111            EventPayload::Stopped { .. } => EventType::Stopped,
112            EventPayload::DebugStarted { .. } => EventType::DebugStarted,
113            EventPayload::Unresolved => EventType::Unresolved,
114        }
115    }
116}
117
118impl From<fcomponent::EventType> for EventType {
119    fn from(fidl_event_type: fcomponent::EventType) -> Self {
120        match fidl_event_type {
121            fcomponent::EventType::CapabilityRequested => EventType::CapabilityRequested,
122            fcomponent::EventType::Discovered => unreachable!("This isn't used anymore"),
123            fcomponent::EventType::Destroyed => EventType::Destroyed,
124            fcomponent::EventType::Resolved => EventType::Resolved,
125            fcomponent::EventType::Started => EventType::Started,
126            fcomponent::EventType::Stopped => EventType::Stopped,
127            fcomponent::EventType::DebugStarted => EventType::DebugStarted,
128            fcomponent::EventType::Unresolved => EventType::Unresolved,
129        }
130    }
131}
132
133impl From<EventType> for fcomponent::EventType {
134    fn from(event_type: EventType) -> Self {
135        match event_type {
136            EventType::CapabilityRequested => fcomponent::EventType::CapabilityRequested,
137            EventType::Destroyed => fcomponent::EventType::Destroyed,
138            EventType::Resolved => fcomponent::EventType::Resolved,
139            EventType::Started => fcomponent::EventType::Started,
140            EventType::Stopped => fcomponent::EventType::Stopped,
141            EventType::DebugStarted => fcomponent::EventType::DebugStarted,
142            EventType::Unresolved => fcomponent::EventType::Unresolved,
143        }
144    }
145}
146
147/// The component manager calls out to objects that implement the `Hook` trait on registered
148/// component manager events. Hooks block the flow of a task, and can mutate, decorate and replace
149/// capabilities. This permits `Hook` to serve as a point of extensibility for the component
150/// manager.
151/// IMPORTANT: Hooks must not block on completion of an Action since Hooks are often called while
152/// executing an Action. Waiting on an Action in a Hook could cause a deadlock.
153/// IMPORTANT: Hooks should avoid causing event dispatch because we do not guarantee serialization
154/// between Hooks. Therefore the order a receiver see events in may be unexpected.
155#[async_trait]
156pub trait Hook: Send + Sync {
157    async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError>;
158}
159
160/// An object registers a hook into a component manager event via a `HooksRegistration` object.
161/// A single object may register for multiple events through a vector of `EventType`. `Hooks`
162/// does not retain the callback. The hook is lazily removed when the callback object loses
163/// strong references.
164#[derive(Clone)]
165pub struct HooksRegistration {
166    events: Vec<EventType>,
167    callback: Weak<dyn Hook>,
168}
169
170impl HooksRegistration {
171    pub fn new(
172        _name: &'static str,
173        events: Vec<EventType>,
174        callback: Weak<dyn Hook>,
175    ) -> HooksRegistration {
176        Self { events, callback }
177    }
178}
179
180/// A [`CapabilityReceiver`] lets a `CapabilityRequested` event subscriber take the
181/// opportunity to monitor requests for the corresponding capability.
182#[derive(Clone)]
183pub struct CapabilityReceiver {
184    inner: Arc<Mutex<Option<Receiver>>>,
185}
186
187impl CapabilityReceiver {
188    /// Creates a [`CapabilityReceiver`] that receives connection requests sent via the
189    /// [`Sender`] capability.
190    pub fn new() -> (Self, Connector) {
191        let (receiver, sender) = Connector::new();
192        let inner = Arc::new(Mutex::new(Some(receiver)));
193        (Self { inner }, sender)
194    }
195
196    /// Take the opportunity to monitor requests.
197    pub fn take(&self) -> Option<Receiver> {
198        self.inner.lock().take()
199    }
200
201    /// Did someone call `take` on this capability receiver.
202    pub fn is_taken(&self) -> bool {
203        self.inner.lock().is_none()
204    }
205}
206
207impl TransferEvent for CapabilityReceiver {
208    fn transfer(&self) -> impl std::future::Future<Output = Self> + Send {
209        async move {
210            let receiver = self.take();
211            let inner = Arc::new(Mutex::new(receiver));
212            Self { inner }
213        }
214    }
215}
216
217#[derive(Clone)]
218pub enum EventPayload {
219    // Keep the events listed below in alphabetical order!
220    CapabilityRequested {
221        source_moniker: Moniker,
222        name: String,
223        receiver: CapabilityReceiver,
224    },
225    Destroyed,
226    Resolved {
227        component: WeakInstanceToken,
228    },
229    Unresolved,
230    Started {
231        runtime: Box<RuntimeInfo>,
232    },
233    Stopped {
234        status: zx::Status,
235        exit_code: Option<i64>,
236        stop_time: zx::BootInstant,
237        stop_time_monotonic: zx::MonotonicInstant,
238        execution_duration: zx::MonotonicDuration,
239        requested_escrow: bool,
240    },
241    DebugStarted {
242        runtime_dir: Option<fio::DirectoryProxy>,
243        break_on_start: Arc<zx::EventPair>,
244    },
245}
246
247/// Information about a component's runtime provided to `Started`.
248#[derive(Clone)]
249pub struct RuntimeInfo {
250    pub diagnostics_receiver: Arc<Mutex<Option<oneshot::Receiver<fcrunner::ComponentDiagnostics>>>>,
251    pub start_time: zx::BootInstant,
252    pub start_time_monotonic: zx::MonotonicInstant,
253}
254
255impl RuntimeInfo {
256    pub fn new(
257        timestamp: zx::BootInstant,
258        timestamp_monotonic: zx::MonotonicInstant,
259        diagnostics_receiver: oneshot::Receiver<fcrunner::ComponentDiagnostics>,
260    ) -> Self {
261        let diagnostics_receiver = Arc::new(Mutex::new(Some(diagnostics_receiver)));
262        Self {
263            diagnostics_receiver,
264            start_time: timestamp,
265            start_time_monotonic: timestamp_monotonic,
266        }
267    }
268}
269
270impl fmt::Debug for EventPayload {
271    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
272        let mut formatter = fmt.debug_struct("EventPayload");
273        formatter.field("type", &self.event_type());
274        match self {
275            EventPayload::CapabilityRequested { name, .. } => {
276                formatter.field("name", &name).finish()
277            }
278            EventPayload::Stopped { status, exit_code, .. } => {
279                formatter.field("status", status).field("exit_code", exit_code).finish()
280            }
281            EventPayload::Unresolved
282            | EventPayload::Destroyed
283            | EventPayload::Started { .. }
284            | EventPayload::Resolved { .. }
285            | EventPayload::DebugStarted { .. } => formatter.finish(),
286        }
287    }
288}
289
290#[derive(Clone, Debug)]
291pub struct Event {
292    /// Moniker of component that this event applies to
293    pub target_moniker: ExtendedMoniker,
294
295    /// Component url of the component that this event applies to
296    pub component_url: Url,
297
298    /// Payload of the event
299    pub payload: EventPayload,
300
301    /// Time when this event was created
302    pub timestamp: zx::BootInstant,
303}
304
305impl Event {
306    pub fn new_builtin(payload: EventPayload) -> Self {
307        Self {
308            target_moniker: ExtendedMoniker::ComponentManager,
309            component_url: "file:///bin/component_manager".parse().unwrap(),
310            payload,
311            timestamp: zx::BootInstant::get(),
312        }
313    }
314}
315
316impl TransferEvent for EventPayload {
317    fn transfer(&self) -> impl std::future::Future<Output = Self> + Send {
318        async move {
319            match self {
320                EventPayload::CapabilityRequested { source_moniker, name, receiver } => {
321                    EventPayload::CapabilityRequested {
322                        source_moniker: source_moniker.clone(),
323                        name: name.to_string(),
324                        receiver: receiver.transfer().await,
325                    }
326                }
327                result => result.clone(),
328            }
329        }
330    }
331}
332
333impl HasEventType for Event {
334    fn event_type(&self) -> EventType {
335        self.payload.event_type()
336    }
337}
338
339impl TransferEvent for Event {
340    fn transfer(&self) -> impl std::future::Future<Output = Self> + Send {
341        async move {
342            Self {
343                target_moniker: self.target_moniker.clone(),
344                component_url: self.component_url.clone(),
345                payload: self.payload.transfer().await,
346                timestamp: self.timestamp,
347            }
348        }
349    }
350}
351
352impl fmt::Display for Event {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        let payload = match &self.payload {
355            EventPayload::CapabilityRequested { source_moniker, name, .. } => {
356                format!("requested '{}' from '{}'", name, source_moniker)
357            }
358            EventPayload::Stopped { status, .. } => {
359                format!("with status: {}", status)
360            }
361            EventPayload::Destroyed { .. }
362            | EventPayload::Resolved { .. }
363            | EventPayload::DebugStarted { .. }
364            | EventPayload::Started { .. }
365            | EventPayload::Unresolved => "".to_string(),
366        };
367        write!(f, "[{}] '{}' {}", self.event_type(), self.target_moniker, payload)
368    }
369}
370
371/// This is a collection of hooks to component manager events.
372pub struct Hooks {
373    hooks_map: Mutex<HashMap<EventType, Vec<Weak<dyn Hook>>>>,
374}
375
376impl Hooks {
377    pub fn new() -> Self {
378        Self { hooks_map: Mutex::new(HashMap::new()) }
379    }
380
381    /// For every hook in `hooks`, add it to the list of hooks that are executed when `dispatch`
382    /// is called for `hook.event`.
383    pub fn install(&self, hooks: Vec<HooksRegistration>) {
384        let mut hooks_map = self.hooks_map.lock();
385        for hook in hooks {
386            for event in hook.events {
387                let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
388                existing_hooks.push(hook.callback.clone());
389            }
390        }
391    }
392
393    /// Same as `install`, but adds the hook to the front of the queue.
394    ///
395    /// This is test-only because in general it shouldn't matter what order hooks are executed
396    /// in. This is useful for tests that need guarantees about hook execution order.
397    pub fn install_front_for_test(&self, hooks: Vec<HooksRegistration>) {
398        let mut hooks_map = self.hooks_map.lock();
399        for hook in hooks {
400            for event in hook.events {
401                let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
402                existing_hooks.insert(0, hook.callback.clone());
403            }
404        }
405    }
406
407    pub async fn dispatch(&self, event: &Event) {
408        let strong_hooks = {
409            let mut hooks_map = self.hooks_map.lock();
410            if let Some(hooks) = hooks_map.get_mut(&event.event_type()) {
411                // We must upgrade our weak references to hooks to strong ones before we can
412                // call out to them.
413                let mut strong_hooks = vec![];
414                hooks.retain(|hook| {
415                    if let Some(hook) = hook.upgrade() {
416                        strong_hooks.push(hook);
417                        true
418                    } else {
419                        false
420                    }
421                });
422                strong_hooks
423            } else {
424                vec![]
425            }
426        };
427        for hook in strong_hooks {
428            if let Err(err) = hook.on(event).await {
429                warn!(err:%, event:%; "Hook produced error for event");
430            }
431        }
432    }
433}
434
435#[cfg(test)]
436mod tests {
437    use super::*;
438
439    // This test verifies that the payload of the CapabilityRequested event will be transferred.
440    #[fuchsia::test]
441    async fn capability_requested_transfer() {
442        let (receiver, _sender) = CapabilityReceiver::new();
443        let event = Event {
444            target_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
445            component_url: "fuchsia-pkg://root".parse().unwrap(),
446            payload: EventPayload::CapabilityRequested {
447                source_moniker: Moniker::root(),
448                name: "foo".to_string(),
449                receiver,
450            },
451            timestamp: zx::BootInstant::get(),
452        };
453
454        // Verify the transferred event carries the capability.
455        let transferred_event = event.transfer().await;
456        match transferred_event.payload {
457            EventPayload::CapabilityRequested { receiver, .. } => {
458                assert!(!receiver.is_taken());
459            }
460            _ => panic!("Event type unexpected"),
461        }
462
463        // Verify that the original event no longer carries the capability.
464        match &event.payload {
465            EventPayload::CapabilityRequested { receiver, .. } => {
466                assert!(receiver.is_taken());
467            }
468            _ => panic!("Event type unexpected"),
469        }
470
471        // Transferring the original event again should give an empty capability provider.
472        let second_transferred_event = event.transfer().await;
473        match &second_transferred_event.payload {
474            EventPayload::CapabilityRequested { receiver, .. } => {
475                assert!(receiver.is_taken());
476            }
477            _ => panic!("Event type unexpected"),
478        }
479    }
480}