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 sandbox::{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.
28#[async_trait]
29pub trait TransferEvent {
30    async fn transfer(&self) -> Self;
31}
32
33#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
34pub enum EventType {
35    /// After a CapabilityProvider has been selected, the CapabilityRequested event is dispatched
36    /// with the ServerEnd of the channel for the capability.
37    CapabilityRequested,
38    /// Destruction of an instance has begun. The instance may/may not be stopped by this point.
39    /// The instance still exists in the parent's realm but will soon be removed.
40    Destroyed,
41    /// An instance's declaration was resolved successfully for the first time.
42    Resolved,
43    /// An instance is about to be started.
44    Started,
45    /// An instance was stopped successfully.
46    /// This event must occur before Destroyed.
47    Stopped,
48    /// Similar to the Started event, except the payload will carry an eventpair
49    /// that the subscriber could use to defer the launch of the component.
50    DebugStarted,
51    /// A component instance was unresolved.
52    Unresolved,
53}
54
55impl EventType {
56    pub fn as_str(&self) -> &str {
57        match self {
58            EventType::CapabilityRequested => "capability_requested",
59            EventType::Destroyed => "destroyed",
60            EventType::Resolved => "resolved",
61            EventType::Started => "started",
62            EventType::Stopped => "stopped",
63            EventType::DebugStarted => "debug_started",
64            EventType::Unresolved => "unresolved",
65        }
66    }
67
68    /// Returns all available event types.
69    pub fn values() -> Vec<EventType> {
70        vec![
71            EventType::CapabilityRequested,
72            EventType::Destroyed,
73            EventType::Resolved,
74            EventType::Started,
75            EventType::Stopped,
76            EventType::DebugStarted,
77            EventType::Unresolved,
78        ]
79    }
80}
81
82impl From<EventType> for Name {
83    fn from(event_type: EventType) -> Name {
84        event_type.as_str().parse().unwrap()
85    }
86}
87
88impl fmt::Display for EventType {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "{}", self.as_str())
91    }
92}
93
94impl TryFrom<String> for EventType {
95    type Error = anyhow::Error;
96    fn try_from(string: String) -> Result<EventType, Self::Error> {
97        for value in EventType::values() {
98            if value.as_str() == string {
99                return Ok(value);
100            }
101        }
102        Err(format_err!("invalid string for event type: {:?}", string))
103    }
104}
105
106impl HasEventType for EventPayload {
107    fn event_type(&self) -> EventType {
108        match self {
109            EventPayload::CapabilityRequested { .. } => EventType::CapabilityRequested,
110            EventPayload::Destroyed => EventType::Destroyed,
111            EventPayload::Resolved { .. } => EventType::Resolved,
112            EventPayload::Started { .. } => EventType::Started,
113            EventPayload::Stopped { .. } => EventType::Stopped,
114            EventPayload::DebugStarted { .. } => EventType::DebugStarted,
115            EventPayload::Unresolved => EventType::Unresolved,
116        }
117    }
118}
119
120impl From<fcomponent::EventType> for EventType {
121    fn from(fidl_event_type: fcomponent::EventType) -> Self {
122        match fidl_event_type {
123            fcomponent::EventType::CapabilityRequested => EventType::CapabilityRequested,
124            fcomponent::EventType::Discovered => unreachable!("This isn't used anymore"),
125            fcomponent::EventType::Destroyed => EventType::Destroyed,
126            fcomponent::EventType::Resolved => EventType::Resolved,
127            fcomponent::EventType::Started => EventType::Started,
128            fcomponent::EventType::Stopped => EventType::Stopped,
129            fcomponent::EventType::DebugStarted => EventType::DebugStarted,
130            fcomponent::EventType::Unresolved => EventType::Unresolved,
131        }
132    }
133}
134
135impl From<EventType> for fcomponent::EventType {
136    fn from(event_type: EventType) -> Self {
137        match event_type {
138            EventType::CapabilityRequested => fcomponent::EventType::CapabilityRequested,
139            EventType::Destroyed => fcomponent::EventType::Destroyed,
140            EventType::Resolved => fcomponent::EventType::Resolved,
141            EventType::Started => fcomponent::EventType::Started,
142            EventType::Stopped => fcomponent::EventType::Stopped,
143            EventType::DebugStarted => fcomponent::EventType::DebugStarted,
144            EventType::Unresolved => fcomponent::EventType::Unresolved,
145        }
146    }
147}
148
149/// The component manager calls out to objects that implement the `Hook` trait on registered
150/// component manager events. Hooks block the flow of a task, and can mutate, decorate and replace
151/// capabilities. This permits `Hook` to serve as a point of extensibility for the component
152/// manager.
153/// IMPORTANT: Hooks must not block on completion of an Action since Hooks are often called while
154/// executing an Action. Waiting on an Action in a Hook could cause a deadlock.
155/// IMPORTANT: Hooks should avoid causing event dispatch because we do not guarantee serialization
156/// between Hooks. Therefore the order a receiver see events in may be unexpected.
157#[async_trait]
158pub trait Hook: Send + Sync {
159    async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError>;
160}
161
162/// An object registers a hook into a component manager event via a `HooksRegistration` object.
163/// A single object may register for multiple events through a vector of `EventType`. `Hooks`
164/// does not retain the callback. The hook is lazily removed when the callback object loses
165/// strong references.
166#[derive(Clone)]
167pub struct HooksRegistration {
168    events: Vec<EventType>,
169    callback: Weak<dyn Hook>,
170}
171
172impl HooksRegistration {
173    pub fn new(
174        _name: &'static str,
175        events: Vec<EventType>,
176        callback: Weak<dyn Hook>,
177    ) -> HooksRegistration {
178        Self { events, callback }
179    }
180}
181
182/// A [`CapabilityReceiver`] lets a `CapabilityRequested` event subscriber take the
183/// opportunity to monitor requests for the corresponding capability.
184#[derive(Clone)]
185pub struct CapabilityReceiver {
186    inner: Arc<Mutex<Option<Receiver>>>,
187}
188
189impl CapabilityReceiver {
190    /// Creates a [`CapabilityReceiver`] that receives connection requests sent via the
191    /// [`Sender`] capability.
192    pub fn new() -> (Self, Connector) {
193        let (receiver, sender) = Connector::new();
194        let inner = Arc::new(Mutex::new(Some(receiver)));
195        (Self { inner }, sender)
196    }
197
198    /// Take the opportunity to monitor requests.
199    pub fn take(&self) -> Option<Receiver> {
200        self.inner.lock().take()
201    }
202
203    /// Did someone call `take` on this capability receiver.
204    pub fn is_taken(&self) -> bool {
205        self.inner.lock().is_none()
206    }
207}
208
209#[async_trait]
210impl TransferEvent for CapabilityReceiver {
211    async fn transfer(&self) -> Self {
212        let receiver = self.take();
213        let inner = Arc::new(Mutex::new(receiver));
214        Self { inner }
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: 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
320#[async_trait]
321impl TransferEvent for EventPayload {
322    async fn transfer(&self) -> Self {
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
336impl HasEventType for Event {
337    fn event_type(&self) -> EventType {
338        self.payload.event_type()
339    }
340}
341
342#[async_trait]
343impl TransferEvent for Event {
344    async fn transfer(&self) -> Self {
345        Self {
346            target_moniker: self.target_moniker.clone(),
347            component_url: self.component_url.clone(),
348            payload: self.payload.transfer().await,
349            timestamp: self.timestamp,
350        }
351    }
352}
353
354impl fmt::Display for Event {
355    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356        let payload = match &self.payload {
357            EventPayload::CapabilityRequested { source_moniker, name, .. } => {
358                format!("requested '{}' from '{}'", name, source_moniker)
359            }
360            EventPayload::Stopped { status, .. } => {
361                format!("with status: {}", status)
362            }
363            EventPayload::Destroyed { .. }
364            | EventPayload::Resolved { .. }
365            | EventPayload::DebugStarted { .. }
366            | EventPayload::Started { .. }
367            | EventPayload::Unresolved => "".to_string(),
368        };
369        write!(f, "[{}] '{}' {}", self.event_type(), self.target_moniker, payload)
370    }
371}
372
373/// This is a collection of hooks to component manager events.
374pub struct Hooks {
375    hooks_map: Mutex<HashMap<EventType, Vec<Weak<dyn Hook>>>>,
376}
377
378impl Hooks {
379    pub fn new() -> Self {
380        Self { hooks_map: Mutex::new(HashMap::new()) }
381    }
382
383    /// For every hook in `hooks`, add it to the list of hooks that are executed when `dispatch`
384    /// is called for `hook.event`.
385    pub fn install(&self, hooks: Vec<HooksRegistration>) {
386        let mut hooks_map = self.hooks_map.lock();
387        for hook in hooks {
388            for event in hook.events {
389                let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
390                existing_hooks.push(hook.callback.clone());
391            }
392        }
393    }
394
395    /// Same as `install`, but adds the hook to the front of the queue.
396    ///
397    /// This is test-only because in general it shouldn't matter what order hooks are executed
398    /// in. This is useful for tests that need guarantees about hook execution order.
399    pub fn install_front_for_test(&self, hooks: Vec<HooksRegistration>) {
400        let mut hooks_map = self.hooks_map.lock();
401        for hook in hooks {
402            for event in hook.events {
403                let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
404                existing_hooks.insert(0, hook.callback.clone());
405            }
406        }
407    }
408
409    pub async fn dispatch(&self, event: &Event) {
410        let strong_hooks = {
411            let mut hooks_map = self.hooks_map.lock();
412            if let Some(hooks) = hooks_map.get_mut(&event.event_type()) {
413                // We must upgrade our weak references to hooks to strong ones before we can
414                // call out to them.
415                let mut strong_hooks = vec![];
416                hooks.retain(|hook| {
417                    if let Some(hook) = hook.upgrade() {
418                        strong_hooks.push(hook);
419                        true
420                    } else {
421                        false
422                    }
423                });
424                strong_hooks
425            } else {
426                vec![]
427            }
428        };
429        for hook in strong_hooks {
430            if let Err(err) = hook.on(event).await {
431                warn!(err:%, event:%; "Hook produced error for event");
432            }
433        }
434    }
435}
436
437#[cfg(test)]
438mod tests {
439    use super::*;
440
441    // This test verifies that the payload of the CapabilityRequested event will be transferred.
442    #[fuchsia::test]
443    async fn capability_requested_transfer() {
444        let (receiver, _sender) = CapabilityReceiver::new();
445        let event = Event {
446            target_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
447            component_url: "fuchsia-pkg://root".parse().unwrap(),
448            payload: EventPayload::CapabilityRequested {
449                source_moniker: Moniker::root(),
450                name: "foo".to_string(),
451                receiver,
452            },
453            timestamp: zx::BootInstant::get(),
454        };
455
456        // Verify the transferred event carries the capability.
457        let transferred_event = event.transfer().await;
458        match transferred_event.payload {
459            EventPayload::CapabilityRequested { receiver, .. } => {
460                assert!(!receiver.is_taken());
461            }
462            _ => panic!("Event type unexpected"),
463        }
464
465        // Verify that the original event no longer carries the capability.
466        match &event.payload {
467            EventPayload::CapabilityRequested { receiver, .. } => {
468                assert!(receiver.is_taken());
469            }
470            _ => panic!("Event type unexpected"),
471        }
472
473        // Transferring the original event again should give an empty capability provider.
474        let second_transferred_event = event.transfer().await;
475        match &second_transferred_event.payload {
476            EventPayload::CapabilityRequested { receiver, .. } => {
477                assert!(receiver.is_taken());
478            }
479            _ => panic!("Event type unexpected"),
480        }
481    }
482}