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