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