1use crate::error::{ComponentInstanceError, RouteVerb, RoutingError};
6use crate::rights::{Rights, validate_rights};
7use async_trait::async_trait;
8use capability_source::CapabilitySource;
9use cm_rust::{CapabilityTypeName, FidlIntoNative, GenericRef, NativeIntoFidl};
10use cm_rust_derive as _;
11use cm_types::RelativePath;
12use fidl_fuchsia_component_runtime as fruntime;
13use fidl_fuchsia_io as fio;
14use moniker::{ChildName, Moniker};
15use router_error::RouterError;
16use runtime_capabilities::{
17 Capability, CapabilityBound, Connector, Data, Dictionary, DirConnector, Routable, Router,
18 RouterErrorInfo, WeakInstanceToken,
19};
20use std::sync::{Arc, Weak};
21
22#[cfg(target_os = "fuchsia")]
23use {cm_types::IterablePath, fuchsia_trace as trace, itertools::Itertools};
24
25pub enum WeakDictionaryOrRouter {
26 Dictionary(Weak<Dictionary>),
27 Router(Weak<Router<Dictionary>>),
28}
29
30impl From<Weak<Dictionary>> for WeakDictionaryOrRouter {
31 fn from(d: Weak<Dictionary>) -> Self {
32 Self::Dictionary(d)
33 }
34}
35
36impl From<Weak<Router<Dictionary>>> for WeakDictionaryOrRouter {
37 fn from(r: Weak<Router<Dictionary>>) -> Self {
38 Self::Router(r)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub struct RouteRequest {
44 pub build_type_name: cm_rust::CapabilityTypeName,
45 pub availability: Option<cm_rust::Availability>,
46 pub directory_rights: Option<fio::Flags>,
47 pub sub_directory_path: Option<RelativePath>,
48 pub event_stream_scope_moniker: Option<Moniker>,
49 pub event_stream_scope: Option<Box<[cm_rust::EventScope]>>,
50}
51
52impl Default for RouteRequest {
53 fn default() -> Self {
54 Self {
55 build_type_name: cm_rust::CapabilityTypeName::Protocol,
60 availability: None,
61 directory_rights: None,
62 sub_directory_path: None,
63 event_stream_scope_moniker: None,
64 event_stream_scope: None,
65 }
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70enum TypeSpecificProperties {
71 DirectoryType(Box<DirectoryProperties>),
73 EventStream(Box<EventStreamProperties>),
75 None,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80enum DirectoryRights {
81 Explicit(fio::Flags),
82 Inherit,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
86struct DirectoryProperties {
87 rights: DirectoryRights,
88 subdir: RelativePath,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93struct EventStreamProperties {
94 moniker: Moniker,
95 scope: Box<[cm_rust::EventScope]>,
96}
97
98pub struct IntermediateRouter {
106 source_dictionary: WeakDictionaryOrRouter,
108
109 source_path: RelativePath,
111
112 default_token: Arc<WeakInstanceToken>,
115
116 moniker: Moniker,
118
119 source: GenericRef,
121
122 type_specific: TypeSpecificProperties,
124
125 type_name: CapabilityTypeName,
126 availability: Option<cm_rust::Availability>,
127 verb: RouteVerb,
128
129 #[allow(unused)]
132 enable_tracing: bool,
133}
134
135#[cfg(target_pointer_width = "64")]
139const _: () = {
140 assert!(std::mem::size_of::<IntermediateRouter>() == 88);
141 assert!(std::mem::size_of::<RouteRequest>() <= 72);
142};
143
144enum RouterCapabilityOrSource<C: CapabilityBound> {
145 Router(Arc<Router<C>>),
146 Capability(Arc<C>),
147 Source(Box<CapabilitySource>),
148}
149
150impl IntermediateRouter {
151 pub fn new(
154 source_dictionary: WeakDictionaryOrRouter,
155 source_path: RelativePath,
156 default_request: RouteRequest,
157 default_token: Arc<WeakInstanceToken>,
158 moniker: Moniker,
159 route_verb: RouteVerb,
160 source: GenericRef,
161 ) -> Capability {
162 assert!(source_path.len() != 0);
163 let RouteRequest {
164 build_type_name: type_name,
165 availability,
166 directory_rights,
167 sub_directory_path,
168 event_stream_scope_moniker,
169 event_stream_scope,
170 } = default_request;
171
172 let type_specific = match type_name {
173 CapabilityTypeName::Directory | CapabilityTypeName::Storage => {
174 let rights = match directory_rights {
175 Some(rights) => DirectoryRights::Explicit(rights),
176 None => DirectoryRights::Inherit,
177 };
178 let subdir = sub_directory_path.unwrap_or_else(RelativePath::dot);
179 TypeSpecificProperties::DirectoryType(Box::new(DirectoryProperties {
180 rights,
181 subdir,
182 }))
183 }
184 CapabilityTypeName::EventStream => {
185 match (event_stream_scope_moniker, event_stream_scope) {
186 (Some(moniker), Some(scope)) => TypeSpecificProperties::EventStream(Box::new(
187 EventStreamProperties { moniker, scope },
188 )),
189 (None, None) => TypeSpecificProperties::None,
190 _ => panic!(
191 "event_stream_scope_moniker and event_stream_scope must both be Some or both be None"
192 ),
193 }
194 }
195 CapabilityTypeName::Protocol
196 | CapabilityTypeName::Service
197 | CapabilityTypeName::Config
198 | CapabilityTypeName::Runner
199 | CapabilityTypeName::Resolver
200 | CapabilityTypeName::Dictionary => TypeSpecificProperties::None,
201 };
202
203 let enable_tracing = route_verb == RouteVerb::Use;
204
205 let self_ = Self {
206 source_dictionary,
207 source_path,
208 default_token,
209 moniker,
210 source,
211 type_specific,
212 type_name,
213 availability,
214 verb: route_verb,
215 enable_tracing,
216 };
217
218 match type_name {
219 CapabilityTypeName::Protocol
220 | CapabilityTypeName::Runner
221 | CapabilityTypeName::Resolver => Router::<Connector>::new(self_).into(),
222 CapabilityTypeName::Service
223 | CapabilityTypeName::Directory
224 | CapabilityTypeName::Storage => Router::<DirConnector>::new(self_).into(),
225 CapabilityTypeName::EventStream | CapabilityTypeName::Dictionary => {
226 Router::<Dictionary>::new(self_).into()
227 }
228 CapabilityTypeName::Config => Router::<Data>::new(self_).into(),
229 }
230 }
231
232 fn default_directory_rights(&self) -> Option<fio::Flags> {
233 match &self.type_specific {
234 TypeSpecificProperties::DirectoryType(props) => match props.rights {
235 DirectoryRights::Explicit(rights) => Some(rights),
236 DirectoryRights::Inherit => None,
237 },
238 _ if self.type_name == CapabilityTypeName::Service => Some(fio::PERM_READABLE),
239 _ => None,
240 }
241 }
242
243 fn default_inherit_rights(&self) -> Option<bool> {
244 match &self.type_specific {
245 TypeSpecificProperties::DirectoryType(props) => match props.rights {
246 DirectoryRights::Inherit => Some(true),
247 DirectoryRights::Explicit(_) => Some(false),
248 },
249 _ if self.type_name == CapabilityTypeName::Service => Some(false),
250 _ => None,
251 }
252 }
253
254 fn default_sub_directory_path(&self) -> Option<RelativePath> {
255 match &self.type_specific {
256 TypeSpecificProperties::DirectoryType(props) => Some(props.subdir.clone()),
257 _ => None,
258 }
259 }
260
261 fn to_not_found_router_error(&self) -> RouterError {
262 RoutingError::RouteSourceNotFound {
263 moniker: self.moniker.clone(),
264 verb: self.verb,
265 counter_verb: match &self.source {
266 GenericRef::Parent => RouteVerb::Offer,
267 GenericRef::Child(_)
268 | GenericRef::ChildInCollection(_, _)
269 | GenericRef::Collection(_) => RouteVerb::Expose,
270 GenericRef::Self_ | GenericRef::Capability(_) => RouteVerb::Declare,
271 GenericRef::Framework
272 | GenericRef::Debug
273 | GenericRef::Void
274 | GenericRef::Environment => RouteVerb::Contain,
275 },
276 source: self.source.clone(),
277 capability_type: self.type_name,
278 capability_name: self.source_path.clone(),
279 }
280 .into()
281 }
282
283 fn default_request_to_fidl(&self) -> fruntime::RouteRequest {
284 let (event_stream_scope_moniker, event_stream_scope) = match &self.type_specific {
285 TypeSpecificProperties::EventStream(props) => (
286 Some(props.moniker.clone().native_into_fidl()),
287 Some(props.scope.clone().native_into_fidl()),
288 ),
289 _ => (None, None),
290 };
291 fruntime::RouteRequest {
292 build_type_name: Some(self.type_name.native_into_fidl()),
293 availability: self.availability.map(NativeIntoFidl::native_into_fidl),
294 directory_rights: self.default_directory_rights(),
295 inherit_rights: self.default_inherit_rights(),
296 sub_directory_path: self
297 .default_sub_directory_path()
298 .map(NativeIntoFidl::native_into_fidl),
299 event_stream_scope_moniker,
300 event_stream_scope,
301 ..Default::default()
302 }
303 }
304
305 fn get_upgrade_failure_moniker(&self) -> Moniker {
307 match &self.source {
308 GenericRef::Parent => self.moniker.parent().unwrap_or_else(|| Moniker::root()),
309
310 GenericRef::Child(name) => {
311 self.moniker.child(ChildName::new(name.clone().into(), None))
312 }
313 GenericRef::ChildInCollection(name, collection) => {
314 self.moniker.child(ChildName::new(name.clone(), Some(collection.clone())))
315 }
316
317 GenericRef::Collection(_)
318 | GenericRef::Capability(_)
319 | GenericRef::Debug
320 | GenericRef::Environment
321 | GenericRef::Framework
322 | GenericRef::Self_
323 | GenericRef::Void => self.moniker.clone(),
324 }
325 }
326
327 async fn upgrade_source_dictionary(
330 &self,
331 dictionary_request: &fruntime::RouteRequest,
332 ) -> Result<Arc<Dictionary>, RouterError> {
333 match &self.source_dictionary {
334 WeakDictionaryOrRouter::Dictionary(dictionary) => dictionary.upgrade().ok_or(
335 RoutingError::from(ComponentInstanceError::InstanceNotFound {
336 moniker: self.get_upgrade_failure_moniker(),
337 })
338 .into(),
339 ),
340 WeakDictionaryOrRouter::Router(router) => {
341 let router = router.upgrade().ok_or(RoutingError::from(
342 ComponentInstanceError::InstanceNotFound {
343 moniker: self.get_upgrade_failure_moniker(),
344 },
345 ))?;
346 let dictionary = router
347 .route(dictionary_request.clone(), self.default_token.clone())
348 .await?
349 .expect("routers for source dictionaries should never return None");
350 Ok(dictionary)
351 }
352 }
353 }
354
355 async fn get_source_router<C: CapabilityBound>(
359 &self,
360 request: &fruntime::RouteRequest,
361 debug: bool,
362 ) -> Result<RouterCapabilityOrSource<C>, RouterError>
363 where
364 Arc<Router<C>>: TryFrom<Capability>,
365 Arc<C>: TryFrom<Capability>,
366 Router<C>: CapabilityBound,
367 {
368 let dictionary_request = fruntime::RouteRequest {
369 build_type_name: Some(CapabilityTypeName::Dictionary.to_string()),
370 ..request.clone()
371 };
372 let mut source_dictionary = self.upgrade_source_dictionary(&dictionary_request).await?;
374
375 let mut path_to_walk = self.source_path.clone();
377 let mut most_recent_source = None;
378 while path_to_walk.len() > 1 {
379 let next_step = path_to_walk.pop_front().expect("we checked that this isn't empty");
380 match source_dictionary.get(&next_step) {
381 Some(Capability::Dictionary(d)) => source_dictionary = d,
382 Some(Capability::DictionaryRouter(r)) => {
383 match r.route(dictionary_request.clone(), self.default_token.clone()).await? {
384 Some(d) => {
385 if debug {
386 most_recent_source = Some(
387 r.route_debug(
388 dictionary_request.clone(),
389 self.default_token.clone(),
390 )
391 .await?,
392 );
393 }
394 source_dictionary = d;
395 }
396 None => {
397 let source = r
400 .route_debug(dictionary_request.clone(), self.default_token.clone())
401 .await?;
402 return Ok(RouterCapabilityOrSource::Source(Box::new(source)));
403 }
404 }
405 }
406 Some(capability) => {
407 return Err(RoutingError::BedrockWrongCapabilityType {
408 actual: capability.debug_typename().to_string(),
409 expected: Dictionary::debug_typename().to_string(),
410 moniker: self.moniker.clone().into(),
411 }
412 .into());
413 }
414 None => {
415 return Err(self.to_not_found_router_error());
416 }
417 }
418 }
419 let capability_name = path_to_walk
420 .pop_front()
421 .expect("we stopped the above loop before fully draining the path");
422 let maybe_source_router = source_dictionary
423 .get(&capability_name)
424 .ok_or_else(|| self.to_not_found_router_error())?;
425
426 let maybe_c: Option<Arc<C>> = maybe_source_router.clone().try_into().ok();
427 if let Some(c) = maybe_c {
428 if !debug {
429 return Ok(RouterCapabilityOrSource::Capability(c));
430 } else {
431 let source = most_recent_source.ok_or_else(|| RoutingError::SourceUnknown {
432 capability_id: capability_name.to_string(),
433 moniker: self.moniker.clone().into(),
434 })?;
435 return Ok(RouterCapabilityOrSource::Source(Box::new(source)));
436 }
437 }
438
439 let capability_type_name = maybe_source_router.debug_typename();
440 let router: Arc<Router<C>> = maybe_source_router.try_into().map_err(|_| {
441 RoutingError::BedrockWrongCapabilityType {
442 actual: capability_type_name.to_string(),
443 expected: Router::<C>::debug_typename().to_string(),
444 moniker: self.moniker.clone().into(),
445 }
446 })?;
447 Ok(RouterCapabilityOrSource::Router(router))
448 }
449
450 fn handle_new_request(&self, request: &mut fruntime::RouteRequest) -> Result<(), RouterError> {
453 if *request == fruntime::RouteRequest::default() {
454 *request = self.default_request_to_fidl();
455 return Ok(());
456 }
457
458 self.check_build_type_name(request)?;
459 self.handle_availability(request)?;
460 self.handle_directory_rights(request)?;
461 self.handle_sub_directory(request)?;
462 self.handle_event_stream_scope(request);
463
464 Ok(())
465 }
466
467 fn check_build_type_name(
468 &self,
469 request: &mut fruntime::RouteRequest,
470 ) -> Result<(), RouterError> {
471 if request.build_type_name != Some(self.type_name.to_string()) {
472 Err(RoutingError::BedrockWrongCapabilityType {
473 moniker: self.moniker.clone().into(),
474 actual: request
475 .build_type_name
476 .as_ref()
477 .map(Clone::clone)
478 .unwrap_or_else(|| "".to_string()),
479 expected: self.type_name.to_string(),
480 })?;
481 }
482 Ok(())
483 }
484
485 fn handle_availability(&self, request: &mut fruntime::RouteRequest) -> Result<(), RouterError> {
486 let Some(default_availability) = self.availability else {
487 return Ok(());
488 };
489 let request_availability: fidl_fuchsia_component_decl::Availability = *request
490 .availability
491 .as_ref()
492 .ok_or_else(|| RoutingError::RouteRequestMissingField {
493 moniker: self.moniker.clone().into(),
494 missing_field: "availability".to_string(),
495 })?;
496 let request_availability: cm_rust::Availability = request_availability.fidl_into_native();
497 let new_availability = crate::availability::advance(
498 &self.moniker.clone().into(),
499 request_availability,
500 default_availability,
501 )
502 .map_err(|e| RoutingError::from(e))?;
503 request.availability = Some(new_availability.native_into_fidl());
504 Ok(())
505 }
506
507 fn handle_directory_rights(
508 &self,
509 request: &mut fruntime::RouteRequest,
510 ) -> Result<(), RouterError> {
511 let Some(directory_rights) = self.default_directory_rights() else {
512 return Ok(());
513 };
514 let rights = Rights::from(directory_rights);
515 validate_rights(self.moniker.clone().into(), rights.into(), request)?;
516 request.directory_intermediate_rights = Some(fio::Flags::from(rights));
517 Ok(())
518 }
519
520 fn handle_sub_directory(
521 &self,
522 request: &mut fruntime::RouteRequest,
523 ) -> Result<(), RouterError> {
524 let Some(mut new_subdir) = self.default_sub_directory_path() else {
525 return Ok(());
526 };
527
528 let Some(current_subdir) = request.sub_directory_path.as_ref() else {
529 request.sub_directory_path = Some(new_subdir.to_string());
530 return Ok(());
531 };
532 let current_subdir = RelativePath::new(current_subdir).map_err(|e| {
533 RoutingError::RouteRequestFailedToParseField {
534 moniker: self.moniker.clone().into(),
535 field: "sub_directory_path".to_string(),
536 parse_error: format!("{e:?}"),
537 }
538 })?;
539
540 let success = new_subdir.extend(current_subdir);
541 if !success {
542 return Err(RoutingError::PathTooLong {
543 moniker: self.moniker.clone().into(),
544 path: self.default_sub_directory_path().unwrap().to_string(),
545 keyword: request.sub_directory_path.clone().unwrap(),
546 }
547 .into());
548 }
549
550 request.sub_directory_path = Some(new_subdir.native_into_fidl());
551 Ok(())
552 }
553
554 fn handle_event_stream_scope(&self, request: &mut fruntime::RouteRequest) {
555 if request.event_stream_scope_moniker.is_some() {
556 return;
559 }
560 if let TypeSpecificProperties::EventStream(props) = &self.type_specific {
561 request.event_stream_scope_moniker = Some(props.moniker.clone().native_into_fidl());
562 request.event_stream_scope = Some(props.scope.clone().native_into_fidl());
563 }
564 }
565}
566
567#[async_trait]
568impl<C: CapabilityBound> Routable<C> for IntermediateRouter
569where
570 Arc<Router<C>>: TryFrom<Capability>,
571 Arc<C>: TryFrom<Capability>,
572 Router<C>: CapabilityBound,
573 C: std::fmt::Debug,
574{
575 async fn route(
576 &self,
577 mut request: fruntime::RouteRequest,
578 target: Arc<WeakInstanceToken>,
579 ) -> Result<Option<Arc<C>>, RouterError> {
580 #[cfg(target_os = "fuchsia")]
581 let _trace_guard = self.enable_tracing.then(|| {
582 trace::async_enter!(
583 trace::Id::new(),
584 "component_manager",
585 "route_capability",
586 "target" => self.moniker.as_str(),
587 "type" => self.type_name.to_string().as_str(),
588 "capability" => self.source_path.iter_segments().join("/").as_str()
589 )
590 });
591
592 self.handle_new_request(&mut request)?;
593 match self.get_source_router(&request, false).await? {
594 RouterCapabilityOrSource::Capability(c) => Ok(Some(c)),
595 RouterCapabilityOrSource::Source(source) => {
596 match &*source {
597 CapabilitySource::Void(_) => (),
598 other_source => {
599 panic!(
600 "should only return source for non-debug routes when source is void, but the source is {other_source:?}"
601 );
602 }
603 }
604 Err(RoutingError::SourceCapabilityIsVoid { moniker: source.source_moniker() }
605 .into())
606 }
607 RouterCapabilityOrSource::Router(router) => router.route(request, target).await,
608 }
609 }
610
611 async fn route_debug(
612 &self,
613 mut request: fruntime::RouteRequest,
614 target: Arc<WeakInstanceToken>,
615 ) -> Result<CapabilitySource, RouterError> {
616 #[cfg(target_os = "fuchsia")]
617 let _trace_guard = self.enable_tracing.then(|| {
618 trace::async_enter!(
619 trace::Id::new(),
620 "component_manager",
621 "route_capability_debug",
622 "target" => self.moniker.as_str(),
623 "type" => self.type_name.to_string().as_str(),
624 "capability" => self.source_path.iter_segments().join("/").as_str()
625 )
626 });
627
628 self.handle_new_request(&mut request)?;
629 match self.get_source_router(&request, true).await? {
630 RouterCapabilityOrSource::Capability(_) => {
631 panic!("returned capability for debug operation")
632 }
633 RouterCapabilityOrSource::Source(source) => Ok(*source),
634 RouterCapabilityOrSource::Router(router) => router.route_debug(request, target).await,
635 }
636 }
637
638 fn error_info(&self) -> Option<RouterErrorInfo> {
639 Some(RouterErrorInfo {
640 capability_type: self.type_name,
641 name: self.source_path.basename().unwrap().to_owned(),
642 availability: self.availability.unwrap(),
643 })
644 }
645}