1use 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#[derive(Clone, Debug, Hash, PartialEq, Eq)]
24pub enum StartReason {
25 AccessCapability { target: Moniker, name: Name },
28 OutgoingDirectory,
31 SingleRun,
33 Debug,
35 Eager,
40 Root,
42 StorageAdmin,
44 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
80pub 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 CapabilityRequested,
91 Destroyed,
94 Resolved,
96 Started,
98 Stopped,
101 DebugStarted,
104 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 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#[async_trait]
211pub trait Hook: Send + Sync {
212 async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError>;
213}
214
215#[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#[derive(Clone)]
238pub struct CapabilityReceiver {
239 inner: Arc<Mutex<Option<Receiver>>>,
240}
241
242impl CapabilityReceiver {
243 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 pub fn take(&self) -> Option<Receiver> {
253 self.inner.lock().take()
254 }
255
256 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 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#[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 pub target_moniker: ExtendedMoniker,
352
353 pub component_url: Url,
355
356 pub payload: EventPayload,
358
359 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
429pub 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 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 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 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 #[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 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 match &event.payload {
523 EventPayload::CapabilityRequested { receiver, .. } => {
524 assert!(receiver.is_taken());
525 }
526 _ => panic!("Event type unexpected"),
527 }
528
529 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}