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