1use crate::bedrock::aggregate_router::{AggregateRouterFn, AggregateSource};
6use crate::bedrock::request_metadata::Metadata;
7use crate::bedrock::structured_dict::{
8 ComponentEnvironment, ComponentInput, ComponentOutput, StructuredDictMap,
9};
10use crate::bedrock::use_dictionary_router::UseDictionaryRouter;
11use crate::bedrock::with_service_renames_and_filter::WithServiceRenamesAndFilter;
12use crate::capability_source::{
13 AggregateCapability, AggregateInstance, AggregateMember, AnonymizedAggregateSource,
14 CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
15 InternalCapability, VoidSource,
16};
17use crate::component_instance::{ComponentInstanceInterface, WeakComponentInstanceInterface};
18use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
19use crate::{DictExt, LazyGet, Sources, WithPorcelain};
20use async_trait::async_trait;
21use cm_rust::{
22 CapabilityTypeName, DictionaryValue, ExposeDecl, ExposeDeclCommon, NativeIntoFidl, OfferDecl,
23 OfferDeclCommon, SourceName, SourcePath, UseDeclCommon,
24};
25use cm_types::{Availability, BorrowedSeparatedPath, IterablePath, Name, SeparatedPath};
26use fidl::endpoints::DiscoverableProtocolMarker;
27use fuchsia_sync::Mutex;
28use futures::FutureExt;
29use itertools::Itertools;
30use log::warn;
31use moniker::{ChildName, Moniker};
32use router_error::RouterError;
33use sandbox::{
34 Capability, CapabilityBound, Connector, Data, Dict, DirConnector, Request, Routable, Router,
35 RouterResponse, WeakInstanceToken,
36};
37use std::collections::{BTreeMap, HashMap};
38use std::fmt::Debug;
39use std::sync::{Arc, LazyLock};
40use {
41 fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
42 fidl_fuchsia_component_internal as finternal, fidl_fuchsia_io as fio,
43 fidl_fuchsia_sys2 as fsys,
44};
45
46pub type EventStreamFilter = Option<BTreeMap<String, DictionaryValue>>;
48
49#[derive(Clone)]
51pub struct EventStreamSourceRouter {
52 pub router: Router<Dict>,
55 pub filter: EventStreamFilter,
58}
59pub type EventStreamUseRouterFn<C> =
60 dyn Fn(&Arc<C>, Vec<EventStreamSourceRouter>) -> Router<Connector>;
61
62static NAMESPACE: LazyLock<Name> = LazyLock::new(|| "namespace".parse().unwrap());
63static NUMBERED_HANDLES: LazyLock<Name> = LazyLock::new(|| "numbered_handles".parse().unwrap());
64static RUNNER: LazyLock<Name> = LazyLock::new(|| "runner".parse().unwrap());
65static CONFIG: LazyLock<Name> = LazyLock::new(|| "config".parse().unwrap());
66
67#[derive(Debug, Clone)]
69pub struct ProgramInput {
70 inner: Dict,
75}
76
77impl Default for ProgramInput {
78 fn default() -> Self {
79 Self::new(Dict::new(), None, Dict::new())
80 }
81}
82
83impl From<ProgramInput> for Dict {
84 fn from(program_input: ProgramInput) -> Self {
85 program_input.inner
86 }
87}
88
89impl ProgramInput {
90 pub fn new(namespace: Dict, runner: Option<Router<Connector>>, config: Dict) -> Self {
91 let inner = Dict::new();
92 inner.insert(NAMESPACE.clone(), namespace.into()).unwrap();
93 if let Some(runner) = runner {
94 inner.insert(RUNNER.clone(), runner.into()).unwrap();
95 }
96 inner.insert(NUMBERED_HANDLES.clone(), Dict::new().into()).unwrap();
97 inner.insert(CONFIG.clone(), config.into()).unwrap();
98 ProgramInput { inner }
99 }
100
101 pub fn namespace(&self) -> Dict {
103 let cap = self.inner.get(&*NAMESPACE).expect("capabilities must be cloneable").unwrap();
104 let Capability::Dictionary(dict) = cap else {
105 unreachable!("namespace entry must be a dict: {cap:?}");
106 };
107 dict
108 }
109
110 pub fn numbered_handles(&self) -> Dict {
112 let cap =
113 self.inner.get(&*NUMBERED_HANDLES).expect("capabilities must be cloneable").unwrap();
114 let Capability::Dictionary(dict) = cap else {
115 unreachable!("numbered_handles entry must be a dict: {cap:?}");
116 };
117 dict
118 }
119
120 pub fn runner(&self) -> Option<Router<Connector>> {
122 let cap = self.inner.get(&*RUNNER).expect("capabilities must be cloneable");
123 match cap {
124 None => None,
125 Some(Capability::ConnectorRouter(r)) => Some(r),
126 cap => unreachable!("runner entry must be a router: {cap:?}"),
127 }
128 }
129
130 fn set_runner(&self, capability: Capability) {
131 self.inner.insert(RUNNER.clone(), capability).unwrap()
132 }
133
134 pub fn config(&self) -> Dict {
136 let cap = self.inner.get(&*CONFIG).expect("capabilities must be cloneable").unwrap();
137 let Capability::Dictionary(dict) = cap else {
138 unreachable!("config entry must be a dict: {cap:?}");
139 };
140 dict
141 }
142}
143
144#[derive(Debug)]
147pub struct ComponentSandbox {
148 pub component_input: ComponentInput,
150
151 pub component_output: ComponentOutput,
153
154 pub program_input: ProgramInput,
156
157 pub program_output_dict: Dict,
159
160 framework_router: Mutex<Router<Dict>>,
170
171 pub capability_sourced_capabilities_dict: Dict,
174
175 pub declared_dictionaries: Dict,
177
178 pub child_inputs: StructuredDictMap<ComponentInput>,
181
182 pub collection_inputs: StructuredDictMap<ComponentInput>,
186}
187
188impl Default for ComponentSandbox {
189 fn default() -> Self {
190 static NULL_ROUTER: LazyLock<Router<Dict>> = LazyLock::new(|| Router::new(NullRouter {}));
191 struct NullRouter;
192 #[async_trait]
193 impl Routable<Dict> for NullRouter {
194 async fn route(
195 &self,
196 _request: Option<Request>,
197 _debug: bool,
198 _target: WeakInstanceToken,
199 ) -> Result<RouterResponse<Dict>, RouterError> {
200 panic!("null router invoked");
201 }
202 }
203 let framework_router = Mutex::new(NULL_ROUTER.clone());
204 Self {
205 framework_router,
206 component_input: Default::default(),
207 component_output: Default::default(),
208 program_input: Default::default(),
209 program_output_dict: Default::default(),
210 capability_sourced_capabilities_dict: Default::default(),
211 declared_dictionaries: Default::default(),
212 child_inputs: Default::default(),
213 collection_inputs: Default::default(),
214 }
215 }
216}
217
218impl Clone for ComponentSandbox {
219 fn clone(&self) -> Self {
220 let Self {
221 component_input,
222 component_output,
223 program_input,
224 program_output_dict,
225 framework_router,
226 capability_sourced_capabilities_dict,
227 declared_dictionaries,
228 child_inputs,
229 collection_inputs,
230 } = self;
231 Self {
232 component_input: component_input.clone(),
233 component_output: component_output.clone(),
234 program_input: program_input.clone(),
235 program_output_dict: program_output_dict.clone(),
236 framework_router: Mutex::new(framework_router.lock().clone()),
237 capability_sourced_capabilities_dict: capability_sourced_capabilities_dict.clone(),
238 declared_dictionaries: declared_dictionaries.clone(),
239 child_inputs: child_inputs.clone(),
240 collection_inputs: collection_inputs.clone(),
241 }
242 }
243}
244
245impl ComponentSandbox {
246 pub fn append(&self, sandbox: &ComponentSandbox) {
249 let ComponentSandbox {
252 component_input,
253 component_output,
254 program_input,
255 program_output_dict,
256 framework_router,
257 capability_sourced_capabilities_dict,
258 declared_dictionaries,
259 child_inputs,
260 collection_inputs,
261 } = sandbox;
262 for (copy_from, copy_to) in [
263 (&component_input.capabilities(), &self.component_input.capabilities()),
264 (&component_input.environment().debug(), &self.component_input.environment().debug()),
265 (
266 &component_input.environment().runners(),
267 &self.component_input.environment().runners(),
268 ),
269 (
270 &component_input.environment().resolvers(),
271 &self.component_input.environment().resolvers(),
272 ),
273 (&component_output.capabilities(), &self.component_output.capabilities()),
274 (&component_output.framework(), &self.component_output.framework()),
275 (&program_input.namespace(), &self.program_input.namespace()),
276 (&program_input.config(), &self.program_input.config()),
277 (&program_output_dict, &self.program_output_dict),
278 (&capability_sourced_capabilities_dict, &self.capability_sourced_capabilities_dict),
279 (&declared_dictionaries, &self.declared_dictionaries),
280 ] {
281 copy_to.append(copy_from).expect("sandbox capability is not cloneable");
282 }
283 if let Some(timeout) = component_input.environment().stop_timeout() {
284 self.component_input.environment().set_stop_timeout(timeout as i64);
285 }
286 *self.framework_router.lock() = framework_router.lock().clone();
287 if let Some(runner_router) = program_input.runner() {
288 self.program_input.set_runner(runner_router.into());
289 }
290 self.child_inputs.append(child_inputs).unwrap();
291 self.collection_inputs.append(collection_inputs).unwrap();
292 }
293
294 pub fn framework_router(&self) -> Router<Dict> {
295 self.framework_router.lock().clone()
296 }
297}
298
299pub fn build_component_sandbox<C: ComponentInstanceInterface + 'static>(
302 component: &Arc<C>,
303 child_component_output_dictionary_routers: HashMap<ChildName, Router<Dict>>,
304 decl: &cm_rust::ComponentDecl,
305 component_input: ComponentInput,
306 program_output_dict: Dict,
307 framework_router: Router<Dict>,
308 capability_sourced_capabilities_dict: Dict,
309 declared_dictionaries: Dict,
310 error_reporter: impl ErrorReporter,
311 aggregate_router_fn: &AggregateRouterFn<C>,
312 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
313) -> ComponentSandbox {
314 let component_output = ComponentOutput::new();
315 let program_input = ProgramInput::default();
316 let environments: StructuredDictMap<ComponentEnvironment> = Default::default();
317 let child_inputs: StructuredDictMap<ComponentInput> = Default::default();
318 let collection_inputs: StructuredDictMap<ComponentInput> = Default::default();
319
320 for environment_decl in &decl.environments {
321 environments
322 .insert(
323 environment_decl.name.clone(),
324 build_environment(
325 component,
326 &child_component_output_dictionary_routers,
327 &component_input,
328 environment_decl,
329 &program_output_dict,
330 &error_reporter,
331 ),
332 )
333 .ok();
334 }
335
336 for child in &decl.children {
337 let environment;
338 if let Some(environment_name) = child.environment.as_ref() {
339 environment = environments.get(environment_name).expect(
340 "child references nonexistent environment, \
341 this should be prevented in manifest validation",
342 );
343 } else {
344 environment = component_input.environment();
345 }
346 let input = ComponentInput::new(environment);
347 let name = Name::new(child.name.as_str()).expect("child is static so name is not long");
348 child_inputs.insert(name, input).ok();
349 }
350
351 for collection in &decl.collections {
352 let environment;
353 if let Some(environment_name) = collection.environment.as_ref() {
354 environment = environments.get(environment_name).expect(
355 "collection references nonexistent environment, \
356 this should be prevented in manifest validation",
357 )
358 } else {
359 environment = component_input.environment();
360 }
361 let input = ComponentInput::new(environment);
362 collection_inputs.insert(collection.name.clone(), input).ok();
363 }
364
365 let mut dictionary_use_bundles = vec![];
366 for use_bundle in group_use_aggregates(&decl.uses).into_iter() {
367 let first_use = *use_bundle.first().unwrap();
368 match first_use {
369 cm_rust::UseDecl::Service(_)
370 if matches!(first_use.source(), cm_rust::UseSource::Collection(_)) =>
371 {
372 let cm_rust::UseSource::Collection(collection_name) = first_use.source() else {
373 unreachable!();
374 };
375 let availability = *first_use.availability();
376 let aggregate = (aggregate_router_fn)(
377 component.clone(),
378 vec![AggregateSource::Collection { collection_name: collection_name.clone() }],
379 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
380 capability: AggregateCapability::Service(first_use.source_name().clone()),
381 moniker: component.moniker().clone(),
382 members: vec![AggregateMember::try_from(first_use).unwrap()],
383 sources: Sources::new(cm_rust::CapabilityTypeName::Service),
384 instances: vec![],
385 }),
386 )
387 .with_porcelain_with_default(CapabilityTypeName::Service)
388 .availability(availability)
389 .target(component)
390 .error_info(first_use)
391 .error_reporter(error_reporter.clone())
392 .build();
393 if let Err(e) = program_input
394 .namespace()
395 .insert_capability(first_use.path().unwrap(), aggregate.into())
396 {
397 warn!(
398 "failed to insert {} in program input dict: {e:?}",
399 first_use.path().unwrap()
400 )
401 }
402 }
403 cm_rust::UseDecl::Service(_) => extend_dict_with_use::<DirConnector, _>(
404 component,
405 &child_component_output_dictionary_routers,
406 &component_input,
407 &program_input,
408 &program_output_dict,
409 &framework_router,
410 &capability_sourced_capabilities_dict,
411 first_use,
412 error_reporter.clone(),
413 ),
414 cm_rust::UseDecl::Directory(_) => extend_dict_with_use::<DirConnector, C>(
415 component,
416 &child_component_output_dictionary_routers,
417 &component_input,
418 &program_input,
419 &program_output_dict,
420 &framework_router,
421 &capability_sourced_capabilities_dict,
422 first_use,
423 error_reporter.clone(),
424 ),
425 cm_rust::UseDecl::Protocol(_) | cm_rust::UseDecl::Runner(_) => {
426 extend_dict_with_use::<Connector, _>(
427 component,
428 &child_component_output_dictionary_routers,
429 &component_input,
430 &program_input,
431 &program_output_dict,
432 &framework_router,
433 &capability_sourced_capabilities_dict,
434 first_use,
435 error_reporter.clone(),
436 )
437 }
438 cm_rust::UseDecl::Config(config) => extend_dict_with_config_use(
439 component,
440 &child_component_output_dictionary_routers,
441 &component_input,
442 &program_input,
443 &program_output_dict,
444 config,
445 error_reporter.clone(),
446 ),
447 cm_rust::UseDecl::EventStream(_) => extend_dict_with_event_stream_uses(
448 component,
449 &component_input,
450 &program_input,
451 use_bundle,
452 error_reporter.clone(),
453 event_stream_use_router_fn,
454 ),
455 cm_rust::UseDecl::Dictionary(_) => {
456 dictionary_use_bundles.push(use_bundle);
457 }
458 _ => (),
459 }
460 }
461
462 if !decl.uses.iter().any(|u| matches!(u, cm_rust::UseDecl::Runner(_))) {
466 if let Some(runner_name) = decl.program.as_ref().and_then(|p| p.runner.as_ref()) {
467 extend_dict_with_use::<Connector, _>(
468 component,
469 &child_component_output_dictionary_routers,
470 &component_input,
471 &program_input,
472 &program_output_dict,
473 &framework_router,
474 &capability_sourced_capabilities_dict,
475 &cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
476 source: cm_rust::UseSource::Environment,
477 source_name: runner_name.clone(),
478 source_dictionary: Default::default(),
479 }),
480 error_reporter.clone(),
481 )
482 }
483 }
484
485 for dictionary_use_bundle in dictionary_use_bundles {
492 extend_dict_with_dictionary_use(
493 component,
494 &child_component_output_dictionary_routers,
495 &component_input,
496 &program_input,
497 &program_output_dict,
498 &framework_router,
499 &capability_sourced_capabilities_dict,
500 dictionary_use_bundle,
501 error_reporter.clone(),
502 )
503 }
504
505 for offer_bundle in group_offer_aggregates(&decl.offers).into_iter() {
506 let first_offer = offer_bundle.first().unwrap();
507 let get_target_dict = || match first_offer.target() {
508 cm_rust::OfferTarget::Child(child_ref) => {
509 assert!(child_ref.collection.is_none(), "unexpected dynamic offer target");
510 let child_name = Name::new(child_ref.name.as_str())
511 .expect("child is static so name is not long");
512 if child_inputs.get(&child_name).is_none() {
513 child_inputs.insert(child_name.clone(), Default::default()).ok();
514 }
515 child_inputs
516 .get(&child_name)
517 .expect("component input was just added")
518 .capabilities()
519 }
520 cm_rust::OfferTarget::Collection(name) => {
521 if collection_inputs.get(&name).is_none() {
522 collection_inputs.insert(name.clone(), Default::default()).ok();
523 }
524 collection_inputs
525 .get(&name)
526 .expect("collection input was just added")
527 .capabilities()
528 }
529 cm_rust::OfferTarget::Capability(name) => {
530 let dict = match declared_dictionaries
531 .get(name)
532 .expect("dictionaries must be cloneable")
533 {
534 Some(dict) => dict,
535 None => {
536 let dict = Dict::new();
537 declared_dictionaries
538 .insert(name.clone(), Capability::Dictionary(dict.clone()))
539 .ok();
540 Capability::Dictionary(dict)
541 }
542 };
543 let Capability::Dictionary(dict) = dict else {
544 panic!("wrong type in dict");
545 };
546 dict
547 }
548 };
549 match first_offer {
550 cm_rust::OfferDecl::Service(_)
551 if offer_bundle.len() == 1
552 && !matches!(first_offer.source(), cm_rust::OfferSource::Collection(_)) =>
553 {
554 extend_dict_with_offer::<DirConnector, _>(
555 component,
556 &child_component_output_dictionary_routers,
557 &component_input,
558 &program_output_dict,
559 &framework_router,
560 &capability_sourced_capabilities_dict,
561 first_offer,
562 &(get_target_dict)(),
563 error_reporter.clone(),
564 )
565 }
566 cm_rust::OfferDecl::Service(_) => {
567 let aggregate_router = new_aggregate_router_from_service_offers(
568 &offer_bundle,
569 component,
570 &child_component_output_dictionary_routers,
571 &component_input,
572 &program_output_dict,
573 &framework_router,
574 &capability_sourced_capabilities_dict,
575 error_reporter.clone(),
576 aggregate_router_fn,
577 );
578 (get_target_dict)()
579 .insert(first_offer.target_name().clone(), aggregate_router.into())
580 .expect("failed to insert capability into target dict");
581 }
582 cm_rust::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
583 component,
584 &child_component_output_dictionary_routers,
585 &component_input,
586 &program_output_dict,
587 &framework_router,
588 &capability_sourced_capabilities_dict,
589 first_offer,
590 &(get_target_dict)(),
591 error_reporter.clone(),
592 ),
593 cm_rust::OfferDecl::Directory(_) => extend_dict_with_offer::<DirConnector, _>(
594 component,
595 &child_component_output_dictionary_routers,
596 &component_input,
597 &program_output_dict,
598 &framework_router,
599 &capability_sourced_capabilities_dict,
600 first_offer,
601 &(get_target_dict)(),
602 error_reporter.clone(),
603 ),
604 cm_rust::OfferDecl::Dictionary(_) | cm_rust::OfferDecl::EventStream(_) => {
605 extend_dict_with_offer::<Dict, _>(
606 component,
607 &child_component_output_dictionary_routers,
608 &component_input,
609 &program_output_dict,
610 &framework_router,
611 &capability_sourced_capabilities_dict,
612 first_offer,
613 &(get_target_dict)(),
614 error_reporter.clone(),
615 )
616 }
617 cm_rust::OfferDecl::Protocol(_)
618 | cm_rust::OfferDecl::Runner(_)
619 | cm_rust::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
620 component,
621 &child_component_output_dictionary_routers,
622 &component_input,
623 &program_output_dict,
624 &framework_router,
625 &capability_sourced_capabilities_dict,
626 first_offer,
627 &(get_target_dict)(),
628 error_reporter.clone(),
629 ),
630 _ => {}
631 }
632 }
633
634 for expose_bundle in group_expose_aggregates(&decl.exposes) {
635 let first_expose = expose_bundle.first().unwrap();
636 match first_expose {
637 cm_rust::ExposeDecl::Service(_)
638 if expose_bundle.len() == 1
639 && !matches!(first_expose.source(), cm_rust::ExposeSource::Collection(_)) =>
640 {
641 extend_dict_with_expose::<DirConnector, _>(
642 component,
643 &child_component_output_dictionary_routers,
644 &program_output_dict,
645 &framework_router,
646 &capability_sourced_capabilities_dict,
647 first_expose,
648 &component_output,
649 error_reporter.clone(),
650 )
651 }
652 cm_rust::ExposeDecl::Service(_) => {
653 let mut aggregate_sources = vec![];
654 let temp_component_output = ComponentOutput::new();
655 for expose in expose_bundle.iter() {
656 extend_dict_with_expose::<DirConnector, _>(
657 component,
658 &child_component_output_dictionary_routers,
659 &program_output_dict,
660 &framework_router,
661 &capability_sourced_capabilities_dict,
662 expose,
663 &temp_component_output,
664 error_reporter.clone(),
665 );
666 match temp_component_output.capabilities().remove(first_expose.target_name()) {
667 Some(Capability::DirConnectorRouter(router)) => {
668 let source_instance = match expose.source() {
669 cm_rust::ExposeSource::Self_ => AggregateInstance::Self_,
670 cm_rust::ExposeSource::Child(name) => AggregateInstance::Child(
671 moniker::ChildName::new(name.clone().to_long(), None),
672 ),
673 other_source => {
674 warn!(
675 "unsupported source found in expose aggregate: {:?}",
676 other_source
677 );
678 continue;
679 }
680 };
681 aggregate_sources
682 .push(AggregateSource::DirectoryRouter { source_instance, router })
683 }
684 None => match expose.source() {
685 cm_rust::ExposeSource::Collection(collection_name) => {
686 aggregate_sources.push(AggregateSource::Collection {
687 collection_name: collection_name.clone(),
688 });
689 }
690 _ => continue,
691 },
692 other_value => panic!("unexpected dictionary entry: {:?}", other_value),
693 }
694 }
695 let availability = *first_expose.availability();
696 let aggregate = (aggregate_router_fn)(
697 component.clone(),
698 aggregate_sources,
699 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
700 capability: AggregateCapability::Service(
701 first_expose.target_name().clone(),
702 ),
703 moniker: component.moniker().clone(),
704 members: expose_bundle
705 .iter()
706 .filter_map(|e| AggregateMember::try_from(*e).ok())
707 .collect(),
708 sources: Sources::new(cm_rust::CapabilityTypeName::Service),
709 instances: vec![],
710 }),
711 );
712 let router = aggregate
713 .with_porcelain_with_default(CapabilityTypeName::Service)
714 .availability(availability)
715 .target(component)
716 .error_info(*first_expose)
717 .error_reporter(error_reporter.clone())
718 .build();
719 component_output
720 .capabilities()
721 .insert(first_expose.target_name().clone(), router.into())
722 .expect("failed to insert capability into target dict")
723 }
724 cm_rust::ExposeDecl::Config(_) => extend_dict_with_expose::<Data, _>(
725 component,
726 &child_component_output_dictionary_routers,
727 &program_output_dict,
728 &framework_router,
729 &capability_sourced_capabilities_dict,
730 first_expose,
731 &component_output,
732 error_reporter.clone(),
733 ),
734 cm_rust::ExposeDecl::Dictionary(_) => extend_dict_with_expose::<Dict, _>(
735 component,
736 &child_component_output_dictionary_routers,
737 &program_output_dict,
738 &framework_router,
739 &capability_sourced_capabilities_dict,
740 first_expose,
741 &component_output,
742 error_reporter.clone(),
743 ),
744 cm_rust::ExposeDecl::Directory(_) => extend_dict_with_expose::<DirConnector, _>(
745 component,
746 &child_component_output_dictionary_routers,
747 &program_output_dict,
748 &framework_router,
749 &capability_sourced_capabilities_dict,
750 first_expose,
751 &component_output,
752 error_reporter.clone(),
753 ),
754 cm_rust::ExposeDecl::Protocol(_)
755 | cm_rust::ExposeDecl::Runner(_)
756 | cm_rust::ExposeDecl::Resolver(_) => extend_dict_with_expose::<Connector, _>(
757 component,
758 &child_component_output_dictionary_routers,
759 &program_output_dict,
760 &framework_router,
761 &capability_sourced_capabilities_dict,
762 first_expose,
763 &component_output,
764 error_reporter.clone(),
765 ),
766 }
767 }
768
769 ComponentSandbox {
770 component_input,
771 component_output,
772 program_input,
773 program_output_dict,
774 framework_router: Mutex::new(framework_router),
775 capability_sourced_capabilities_dict,
776 declared_dictionaries,
777 child_inputs,
778 collection_inputs,
779 }
780}
781
782fn new_aggregate_router_from_service_offers<C: ComponentInstanceInterface + 'static>(
783 offer_bundle: &Vec<&cm_rust::OfferDecl>,
784 component: &Arc<C>,
785 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
786 component_input: &ComponentInput,
787 program_output_dict: &Dict,
788 framework_router: &Router<Dict>,
789 capability_sourced_capabilities_dict: &Dict,
790 error_reporter: impl ErrorReporter,
791 aggregate_router_fn: &AggregateRouterFn<C>,
792) -> Router<DirConnector> {
793 let mut aggregate_sources = vec![];
794 let dict_for_source_router = Dict::new();
795 let source = new_aggregate_capability_source(component.moniker().clone(), offer_bundle.clone());
796 for offer in offer_bundle.iter() {
797 if matches!(&source, &CapabilitySource::FilteredAggregateProvider(_)) {
798 if let cm_rust::OfferDecl::Service(offer_service_decl) = offer {
799 if offer_service_decl
800 .source_instance_filter
801 .as_ref()
802 .and_then(|v| v.first())
803 .is_none()
804 && offer_service_decl
805 .renamed_instances
806 .as_ref()
807 .and_then(|v| v.first())
808 .is_none()
809 {
810 continue;
814 }
815 }
816 }
817 extend_dict_with_offer::<DirConnector, _>(
818 component,
819 &child_component_output_dictionary_routers,
820 &component_input,
821 &program_output_dict,
822 framework_router,
823 &capability_sourced_capabilities_dict,
824 offer,
825 &dict_for_source_router,
826 error_reporter.clone(),
827 );
828 match dict_for_source_router.remove(offer.target_name()) {
829 Some(Capability::DirConnectorRouter(router)) => {
830 let source_instance = match offer.source() {
831 cm_rust::OfferSource::Self_ => AggregateInstance::Self_,
832 cm_rust::OfferSource::Parent => AggregateInstance::Parent,
833 cm_rust::OfferSource::Child(child_ref) => {
834 AggregateInstance::Child(moniker::ChildName::new(
835 child_ref.name.clone(),
836 child_ref.collection.clone(),
837 ))
838 }
839 other_source => {
840 warn!("unsupported source found in offer aggregate: {:?}", other_source);
841 continue;
842 }
843 };
844 aggregate_sources.push(AggregateSource::DirectoryRouter { source_instance, router })
845 }
846 None => match offer.source() {
847 cm_rust::OfferSource::Collection(collection_name) => {
851 aggregate_sources.push(AggregateSource::Collection {
852 collection_name: collection_name.clone(),
853 });
854 }
855 _ => continue,
856 },
857 other => warn!("found unexpected entry in dictionary: {:?}", other),
858 }
859 }
860 (aggregate_router_fn)(component.clone(), aggregate_sources, source)
861}
862
863fn new_aggregate_capability_source(
864 moniker: Moniker,
865 offers: Vec<&cm_rust::OfferDecl>,
866) -> CapabilitySource {
867 let offer_service_decls = offers
868 .iter()
869 .map(|o| match o {
870 cm_rust::OfferDecl::Service(o) => o,
871 _ => panic!(
872 "cannot aggregate non-service capabilities, manifest validation should prevent this"
873 ),
874 })
875 .collect::<Vec<_>>();
876 let is_filtered_offer = offer_service_decls.iter().any(|o| {
878 o.source_instance_filter.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
879 || o.renamed_instances.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
880 });
881 let capability =
882 AggregateCapability::Service(offer_service_decls.first().unwrap().target_name.clone());
883 if is_filtered_offer {
884 CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
885 capability,
886 moniker,
887 offer_service_decls: offer_service_decls.into_iter().cloned().collect(),
888 sources: Sources::new(cm_rust::CapabilityTypeName::Service).component().collection(),
889 })
890 } else {
891 let members = offers.iter().filter_map(|o| AggregateMember::try_from(*o).ok()).collect();
892 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
893 capability,
894 moniker,
895 members,
896 sources: Sources::new(cm_rust::CapabilityTypeName::Service).component().collection(),
897 instances: vec![],
898 })
899 }
900}
901
902fn group_use_aggregates(uses: &[cm_rust::UseDecl]) -> Vec<Vec<&cm_rust::UseDecl>> {
906 let mut groupings = HashMap::new();
907 let mut ungroupable_uses = vec![];
908 for use_ in uses.iter() {
909 if let Some(target_path) = use_.path() {
910 groupings.entry(target_path).or_insert(vec![]).push(use_);
911 } else {
912 ungroupable_uses.push(vec![use_]);
913 }
914 }
915 groupings
916 .into_iter()
917 .map(|(_key, grouping)| grouping)
918 .chain(ungroupable_uses.into_iter())
919 .collect()
920}
921
922fn group_offer_aggregates(offers: &[cm_rust::OfferDecl]) -> Vec<Vec<&cm_rust::OfferDecl>> {
926 let mut groupings = HashMap::new();
927 for offer in offers {
928 groupings.entry((offer.target(), offer.target_name())).or_insert(vec![]).push(offer);
929 }
930 groupings.into_iter().map(|(_key, grouping)| grouping).collect()
931}
932
933fn group_expose_aggregates(exposes: &[cm_rust::ExposeDecl]) -> Vec<Vec<&cm_rust::ExposeDecl>> {
935 let mut groupings = HashMap::new();
936 for expose in exposes {
937 groupings.entry((expose.target(), expose.target_name())).or_insert(vec![]).push(expose);
938 }
939 groupings.into_iter().map(|(_key, grouping)| grouping).collect()
940}
941
942fn build_environment<C: ComponentInstanceInterface + 'static>(
943 component: &Arc<C>,
944 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
945 component_input: &ComponentInput,
946 environment_decl: &cm_rust::EnvironmentDecl,
947 program_output_dict: &Dict,
948 error_reporter: &impl ErrorReporter,
949) -> ComponentEnvironment {
950 let mut environment = ComponentEnvironment::new();
951 if environment_decl.extends == fdecl::EnvironmentExtends::Realm {
952 if let Ok(e) = component_input.environment().shallow_copy() {
953 environment = e;
954 } else {
955 warn!("failed to copy component_input.environment");
956 }
957 }
958 environment.set_name(&environment_decl.name);
959 if let Some(stop_timeout_ms) = environment_decl.stop_timeout_ms {
960 environment.set_stop_timeout(stop_timeout_ms as i64);
961 }
962 let debug = environment_decl.debug_capabilities.iter().map(|debug_registration| {
963 let cm_rust::DebugRegistration::Protocol(debug) = debug_registration;
964 (
965 &debug.source_name,
966 debug.target_name.clone(),
967 &debug.source,
968 CapabilityTypeName::Protocol,
969 RouteRequestErrorInfo::from(debug_registration),
970 )
971 });
972 let runners = environment_decl.runners.iter().map(|runner| {
973 (
974 &runner.source_name,
975 runner.target_name.clone(),
976 &runner.source,
977 CapabilityTypeName::Runner,
978 RouteRequestErrorInfo::from(runner),
979 )
980 });
981 let resolvers = environment_decl.resolvers.iter().map(|resolver| {
982 (
983 &resolver.resolver,
984 Name::new(&resolver.scheme).unwrap(),
985 &resolver.source,
986 CapabilityTypeName::Resolver,
987 RouteRequestErrorInfo::from(resolver),
988 )
989 });
990 let moniker = component.moniker();
991 for (source_name, target_name, source, porcelain_type, route_request) in
992 debug.chain(runners).chain(resolvers)
993 {
994 let source_path =
995 SeparatedPath { dirname: Default::default(), basename: source_name.clone() };
996 let router: Router<Connector> = match &source {
997 cm_rust::RegistrationSource::Parent => {
998 use_from_parent_router::<Connector>(component_input, source_path, moniker)
999 }
1000 cm_rust::RegistrationSource::Self_ => program_output_dict
1001 .get_router_or_not_found::<Connector>(
1002 &source_path,
1003 RoutingError::use_from_self_not_found(
1004 moniker,
1005 source_path.iter_segments().join("/"),
1006 ),
1007 ),
1008 cm_rust::RegistrationSource::Child(child_name) => {
1009 let child_name = ChildName::parse(child_name).expect("invalid child name");
1010 let Some(child_component_output) =
1011 child_component_output_dictionary_routers.get(&child_name)
1012 else {
1013 continue;
1014 };
1015 child_component_output.clone().lazy_get(
1016 source_path,
1017 RoutingError::use_from_child_expose_not_found(
1018 &child_name,
1019 moniker,
1020 source_name.clone(),
1021 ),
1022 )
1023 }
1024 };
1025 let router = router
1026 .with_porcelain_no_default(porcelain_type)
1027 .availability(Availability::Required)
1028 .target(component)
1029 .error_info(route_request)
1030 .error_reporter(error_reporter.clone());
1031 let dict_to_insert_to = match porcelain_type {
1032 CapabilityTypeName::Protocol => environment.debug(),
1033 CapabilityTypeName::Runner => environment.runners(),
1034 CapabilityTypeName::Resolver => environment.resolvers(),
1035 c => panic!("unexpected capability type {}", c),
1036 };
1037 match dict_to_insert_to.insert_capability(&target_name, router.into()) {
1038 Ok(()) => (),
1039 Err(_e) => {
1040 }
1044 }
1045 }
1046 environment
1047}
1048
1049pub fn extend_dict_with_offers<C: ComponentInstanceInterface + 'static>(
1051 component: &Arc<C>,
1052 static_offers: &[cm_rust::OfferDecl],
1053 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1054 component_input: &ComponentInput,
1055 dynamic_offers: &[cm_rust::OfferDecl],
1056 program_output_dict: &Dict,
1057 framework_router: &Router<Dict>,
1058 capability_sourced_capabilities_dict: &Dict,
1059 target_input: &ComponentInput,
1060 error_reporter: impl ErrorReporter,
1061 aggregate_router_fn: &AggregateRouterFn<C>,
1062) {
1063 for offer_bundle in group_offer_aggregates(dynamic_offers).into_iter() {
1064 let first_offer = offer_bundle.first().unwrap();
1065 match first_offer {
1066 cm_rust::OfferDecl::Service(_) => {
1067 let static_offer_bundles = group_offer_aggregates(static_offers);
1068 let maybe_static_offer_bundle = static_offer_bundles.into_iter().find(|bundle| {
1069 bundle.first().unwrap().target_name() == first_offer.target_name()
1070 });
1071 let mut combined_offer_bundle = offer_bundle.clone();
1072 if let Some(mut static_offer_bundle) = maybe_static_offer_bundle {
1073 let _ = target_input.capabilities().remove(first_offer.target_name());
1078 combined_offer_bundle.append(&mut static_offer_bundle);
1079 }
1080 if combined_offer_bundle.len() == 1
1081 && !matches!(first_offer.source(), cm_rust::OfferSource::Collection(_))
1082 {
1083 extend_dict_with_offer::<DirConnector, _>(
1084 component,
1085 &child_component_output_dictionary_routers,
1086 &component_input,
1087 &program_output_dict,
1088 framework_router,
1089 &capability_sourced_capabilities_dict,
1090 first_offer,
1091 &target_input.capabilities(),
1092 error_reporter.clone(),
1093 )
1094 } else {
1095 let aggregate_router = new_aggregate_router_from_service_offers(
1096 &combined_offer_bundle,
1097 component,
1098 &child_component_output_dictionary_routers,
1099 &component_input,
1100 &program_output_dict,
1101 framework_router,
1102 &capability_sourced_capabilities_dict,
1103 error_reporter.clone(),
1104 aggregate_router_fn,
1105 );
1106 target_input
1107 .capabilities()
1108 .insert(first_offer.target_name().clone(), aggregate_router.into())
1109 .expect("failed to insert capability into target dict");
1110 }
1111 }
1112 cm_rust::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
1113 component,
1114 &child_component_output_dictionary_routers,
1115 component_input,
1116 program_output_dict,
1117 framework_router,
1118 capability_sourced_capabilities_dict,
1119 first_offer,
1120 &target_input.capabilities(),
1121 error_reporter.clone(),
1122 ),
1123 cm_rust::OfferDecl::Dictionary(_) => extend_dict_with_offer::<Dict, _>(
1124 component,
1125 &child_component_output_dictionary_routers,
1126 component_input,
1127 program_output_dict,
1128 framework_router,
1129 capability_sourced_capabilities_dict,
1130 first_offer,
1131 &target_input.capabilities(),
1132 error_reporter.clone(),
1133 ),
1134 cm_rust::OfferDecl::Directory(_) => extend_dict_with_offer::<DirConnector, _>(
1135 component,
1136 &child_component_output_dictionary_routers,
1137 component_input,
1138 program_output_dict,
1139 framework_router,
1140 capability_sourced_capabilities_dict,
1141 first_offer,
1142 &target_input.capabilities(),
1143 error_reporter.clone(),
1144 ),
1145 cm_rust::OfferDecl::Protocol(_)
1146 | cm_rust::OfferDecl::Runner(_)
1147 | cm_rust::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
1148 component,
1149 &child_component_output_dictionary_routers,
1150 component_input,
1151 program_output_dict,
1152 framework_router,
1153 capability_sourced_capabilities_dict,
1154 first_offer,
1155 &target_input.capabilities(),
1156 error_reporter.clone(),
1157 ),
1158 _ => {}
1159 }
1160 }
1161}
1162
1163pub fn is_supported_use(use_: &cm_rust::UseDecl) -> bool {
1164 matches!(
1165 use_,
1166 cm_rust::UseDecl::Config(_)
1167 | cm_rust::UseDecl::Protocol(_)
1168 | cm_rust::UseDecl::Runner(_)
1169 | cm_rust::UseDecl::Service(_)
1170 | cm_rust::UseDecl::Directory(_)
1171 | cm_rust::UseDecl::EventStream(_)
1172 | cm_rust::UseDecl::Dictionary(_)
1173 )
1174}
1175
1176fn extend_dict_with_config_use<C: ComponentInstanceInterface + 'static>(
1179 component: &Arc<C>,
1180 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1181 component_input: &ComponentInput,
1182 program_input: &ProgramInput,
1183 program_output_dict: &Dict,
1184 config_use: &cm_rust::UseConfigurationDecl,
1185 error_reporter: impl ErrorReporter,
1186) {
1187 let moniker = component.moniker();
1188 let source_path = config_use.source_path();
1189 let porcelain_type = CapabilityTypeName::Config;
1190 let router: Router<Data> = match config_use.source() {
1191 cm_rust::UseSource::Parent => {
1192 use_from_parent_router::<Data>(component_input, source_path.to_owned(), moniker)
1193 }
1194 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<Data>(
1195 &source_path,
1196 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1197 ),
1198 cm_rust::UseSource::Child(child_name) => {
1199 let child_name = ChildName::parse(child_name).expect("invalid child name");
1200 let Some(child_component_output) =
1201 child_component_output_dictionary_routers.get(&child_name)
1202 else {
1203 panic!(
1204 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1205 moniker, child_name
1206 );
1207 };
1208 child_component_output.clone().lazy_get(
1209 source_path.to_owned(),
1210 RoutingError::use_from_child_expose_not_found(
1211 &child_name,
1212 &moniker,
1213 config_use.source_name().clone(),
1214 ),
1215 )
1216 }
1217 cm_rust::UseSource::Environment => return,
1219 cm_rust::UseSource::Debug => return,
1220 cm_rust::UseSource::Capability(_) => return,
1221 cm_rust::UseSource::Framework => return,
1222 cm_rust::UseSource::Collection(_) => return,
1223 };
1224
1225 let availability = *config_use.availability();
1226 match program_input.config().insert_capability(
1227 &config_use.target_name,
1228 router
1229 .with_porcelain_with_default(porcelain_type)
1230 .availability(availability)
1231 .target(component)
1232 .error_info(config_use)
1233 .error_reporter(error_reporter)
1234 .into(),
1235 ) {
1236 Ok(()) => (),
1237 Err(e) => {
1238 warn!("failed to insert {} in program input dict: {e:?}", config_use.target_name)
1239 }
1240 }
1241}
1242
1243fn extend_dict_with_event_stream_uses<C: ComponentInstanceInterface + 'static>(
1244 component: &Arc<C>,
1245 component_input: &ComponentInput,
1246 program_input: &ProgramInput,
1247 uses: Vec<&cm_rust::UseDecl>,
1248 error_reporter: impl ErrorReporter,
1249 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
1250) {
1251 let use_event_stream_decls = uses.into_iter().map(|u| match u {
1252 cm_rust::UseDecl::EventStream(decl) => decl,
1253 _other_use => panic!("conflicting use types share target path, this should be prevented by manifest validation"),
1254 }).collect::<Vec<_>>();
1255 let moniker = component.moniker();
1256 let porcelain_type = CapabilityTypeName::EventStream;
1257 let target_path = use_event_stream_decls.first().unwrap().target_path.clone();
1258 for use_event_stream_decl in &use_event_stream_decls {
1259 assert_eq!(
1260 &use_event_stream_decl.source,
1261 &cm_rust::UseSource::Parent,
1262 "event streams can only be used from parent, anything else should be caught by \
1263 manifest validation",
1264 );
1265 }
1266 let routers = use_event_stream_decls
1267 .into_iter()
1268 .map(|use_event_stream_decl| {
1269 let mut route_metadata = finternal::EventStreamRouteMetadata::default();
1270 if let Some(scope) = &use_event_stream_decl.scope {
1271 route_metadata.scope_moniker = Some(component.moniker().to_string());
1272 route_metadata.scope = Some(scope.clone().native_into_fidl());
1273 }
1274
1275 let source_path = use_event_stream_decl.source_path().to_owned();
1276 let router = use_from_parent_router::<Dict>(component_input, source_path, &moniker)
1277 .with_porcelain_with_default(porcelain_type)
1278 .availability(use_event_stream_decl.availability)
1279 .event_stream_route_metadata(route_metadata)
1280 .target(component)
1281 .error_info(RouteRequestErrorInfo::from(use_event_stream_decl))
1282 .error_reporter(error_reporter.clone())
1283 .build();
1284 let filter = use_event_stream_decl.filter.clone();
1285 EventStreamSourceRouter { router, filter }
1286 })
1287 .collect::<Vec<_>>();
1288
1289 let router = event_stream_use_router_fn(component, routers);
1290 if let Err(e) = program_input.namespace().insert_capability(&target_path, router.into()) {
1291 warn!("failed to insert {} in program input dict: {e:?}", target_path)
1292 }
1293}
1294
1295fn extend_dict_with_use<T, C: ComponentInstanceInterface + 'static>(
1296 component: &Arc<C>,
1297 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1298 component_input: &ComponentInput,
1299 program_input: &ProgramInput,
1300 program_output_dict: &Dict,
1301 framework_router: &Router<Dict>,
1302 capability_sourced_capabilities_dict: &Dict,
1303 use_: &cm_rust::UseDecl,
1304 error_reporter: impl ErrorReporter,
1305) where
1306 T: CapabilityBound + Clone,
1307 Router<T>: TryFrom<Capability> + Into<Capability>,
1308{
1309 if !is_supported_use(use_) {
1310 return;
1311 }
1312 let moniker = component.moniker();
1313 if let cm_rust::UseDecl::Config(config) = use_ {
1314 return extend_dict_with_config_use(
1315 component,
1316 child_component_output_dictionary_routers,
1317 component_input,
1318 program_input,
1319 program_output_dict,
1320 config,
1321 error_reporter,
1322 );
1323 };
1324
1325 let source_path = use_.source_path();
1326 let porcelain_type = CapabilityTypeName::from(use_);
1327 let router: Router<T> = match use_.source() {
1328 cm_rust::UseSource::Parent => {
1329 use_from_parent_router::<T>(component_input, source_path.to_owned(), moniker)
1330 }
1331 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1332 &source_path,
1333 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1334 ),
1335 cm_rust::UseSource::Child(child_name) => {
1336 let child_name = ChildName::parse(child_name).expect("invalid child name");
1337 let Some(child_component_output) =
1338 child_component_output_dictionary_routers.get(&child_name)
1339 else {
1340 panic!(
1341 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1342 moniker, child_name
1343 );
1344 };
1345 child_component_output.clone().lazy_get(
1346 source_path.to_owned(),
1347 RoutingError::use_from_child_expose_not_found(
1348 &child_name,
1349 &moniker,
1350 use_.source_name().clone(),
1351 ),
1352 )
1353 }
1354 cm_rust::UseSource::Framework if use_.is_from_dictionary() => {
1355 Router::<T>::new_error(RoutingError::capability_from_framework_not_found(
1356 moniker,
1357 source_path.iter_segments().join("/"),
1358 ))
1359 }
1360 cm_rust::UseSource::Framework => {
1361 query_framework_router_or_not_found(framework_router, &source_path, component)
1362 }
1363 cm_rust::UseSource::Capability(capability_name) => {
1364 let err = RoutingError::capability_from_capability_not_found(
1365 moniker,
1366 capability_name.as_str().to_string(),
1367 );
1368 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1369 || source_path.iter_segments().join("/")
1370 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1371 {
1372 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1373 } else {
1374 Router::<T>::new_error(err)
1375 }
1376 }
1377 cm_rust::UseSource::Debug => {
1378 let cm_rust::UseDecl::Protocol(use_protocol) = use_ else {
1379 panic!(
1380 "non-protocol capability used with a debug source, this should be prevented by manifest validation"
1381 );
1382 };
1383 component_input.environment().debug().get_router_or_not_found::<T>(
1384 &use_protocol.source_name,
1385 RoutingError::use_from_environment_not_found(
1386 moniker,
1387 "protocol",
1388 &use_protocol.source_name,
1389 ),
1390 )
1391 }
1392 cm_rust::UseSource::Environment => {
1393 let cm_rust::UseDecl::Runner(use_runner) = use_ else {
1394 panic!(
1395 "non-runner capability used with an environment source, this should be prevented by manifest validation"
1396 );
1397 };
1398 component_input.environment().runners().get_router_or_not_found::<T>(
1399 &use_runner.source_name,
1400 RoutingError::use_from_environment_not_found(
1401 moniker,
1402 "runner",
1403 &use_runner.source_name,
1404 ),
1405 )
1406 }
1407 cm_rust::UseSource::Collection(_) => {
1408 return;
1410 }
1411 };
1412
1413 let availability = *use_.availability();
1414 let mut router_builder = router
1415 .with_porcelain_with_default(porcelain_type)
1416 .availability(availability)
1417 .target(&component)
1418 .error_info(use_)
1419 .error_reporter(error_reporter);
1420 if let cm_rust::UseDecl::Directory(decl) = use_ {
1421 router_builder = router_builder
1422 .rights(Some(decl.rights.into()))
1423 .subdir(decl.subdir.clone().into())
1424 .inherit_rights(false);
1425 }
1426 if let cm_rust::UseDecl::Service(_) = use_ {
1427 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(false);
1428 }
1429 let router = router_builder.build();
1430
1431 match use_ {
1432 cm_rust::UseDecl::Protocol(cm_rust::UseProtocolDecl {
1433 numbered_handle: Some(numbered_handle),
1434 ..
1435 }) => {
1436 let numbered_handle = Name::from(*numbered_handle);
1437 if let Err(e) =
1438 program_input.numbered_handles().insert_capability(&numbered_handle, router.into())
1439 {
1440 warn!("failed to insert {} in program input dict: {e:?}", numbered_handle)
1441 }
1442 }
1443 cm_rust::UseDecl::Runner(_) => {
1444 assert!(program_input.runner().is_none(), "component can't use multiple runners");
1445 program_input.set_runner(router.into());
1446 }
1447 _ => {
1448 if let Err(e) =
1449 program_input.namespace().insert_capability(use_.path().unwrap(), router.into())
1450 {
1451 warn!("failed to insert {} in program input dict: {e:?}", use_.path().unwrap())
1452 }
1453 }
1454 }
1455}
1456
1457fn extend_dict_with_dictionary_use<C: ComponentInstanceInterface + 'static>(
1458 component: &Arc<C>,
1459 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1460 component_input: &ComponentInput,
1461 program_input: &ProgramInput,
1462 program_output_dict: &Dict,
1463 framework_router: &Router<Dict>,
1464 capability_sourced_capabilities_dict: &Dict,
1465 use_bundle: Vec<&cm_rust::UseDecl>,
1466 error_reporter: impl ErrorReporter,
1467) {
1468 let path = use_bundle[0].path().unwrap();
1469 let mut dictionary_routers = vec![];
1470 for use_ in use_bundle.iter() {
1471 let dict_for_used_router = ProgramInput::new(Dict::new(), None, Dict::new());
1472 extend_dict_with_use::<Dict, _>(
1473 component,
1474 child_component_output_dictionary_routers,
1475 component_input,
1476 &dict_for_used_router,
1477 program_output_dict,
1478 framework_router,
1479 capability_sourced_capabilities_dict,
1480 use_,
1481 error_reporter.clone(),
1482 );
1483 let dictionary_router = match dict_for_used_router.namespace().get_capability(path) {
1484 Some(Capability::DictionaryRouter(router)) => router,
1485 other_value => panic!("unexpected dictionary get result: {other_value:?}"),
1486 };
1487 dictionary_routers.push(dictionary_router);
1488 }
1489 let original_dictionary = match program_input.namespace().get_capability(path) {
1490 Some(Capability::Dictionary(dictionary)) => dictionary,
1491 _ => Dict::new(),
1492 };
1493 let router = UseDictionaryRouter::new(
1494 path.clone(),
1495 component.moniker().clone(),
1496 original_dictionary,
1497 dictionary_routers,
1498 CapabilitySource::Component(ComponentSource {
1499 capability: ComponentCapability::Use_((*use_bundle.first().unwrap()).clone()),
1500 moniker: component.moniker().clone(),
1501 }),
1502 );
1503 match program_input.namespace().insert_capability(path, router.into()) {
1504 Ok(()) => (),
1505 Err(_e) => {
1506 }
1510 }
1511}
1512
1513fn use_from_parent_router<T>(
1515 component_input: &ComponentInput,
1516 source_path: impl IterablePath + 'static + Debug,
1517 moniker: &Moniker,
1518) -> Router<T>
1519where
1520 T: CapabilityBound + Clone,
1521 Router<T>: TryFrom<Capability>,
1522{
1523 let err = if moniker == &Moniker::root() {
1524 RoutingError::register_from_component_manager_not_found(
1525 source_path.iter_segments().join("/"),
1526 )
1527 } else {
1528 RoutingError::use_from_parent_not_found(moniker, source_path.iter_segments().join("/"))
1529 };
1530 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1531}
1532
1533fn is_supported_offer(offer: &cm_rust::OfferDecl) -> bool {
1534 matches!(
1535 offer,
1536 cm_rust::OfferDecl::Config(_)
1537 | cm_rust::OfferDecl::Protocol(_)
1538 | cm_rust::OfferDecl::Dictionary(_)
1539 | cm_rust::OfferDecl::Directory(_)
1540 | cm_rust::OfferDecl::Runner(_)
1541 | cm_rust::OfferDecl::Resolver(_)
1542 | cm_rust::OfferDecl::Service(_)
1543 | cm_rust::OfferDecl::EventStream(_)
1544 )
1545}
1546
1547fn extend_dict_with_offer<T, C: ComponentInstanceInterface + 'static>(
1548 component: &Arc<C>,
1549 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1550 component_input: &ComponentInput,
1551 program_output_dict: &Dict,
1552 framework_router: &Router<Dict>,
1553 capability_sourced_capabilities_dict: &Dict,
1554 offer: &cm_rust::OfferDecl,
1555 target_dict: &Dict,
1556 error_reporter: impl ErrorReporter,
1557) where
1558 T: CapabilityBound + Clone,
1559 Router<T>: TryFrom<Capability> + Into<Capability> + WithServiceRenamesAndFilter,
1560{
1561 assert!(is_supported_offer(offer), "{offer:?}");
1562
1563 let source_path = offer.source_path();
1564 let target_name = offer.target_name();
1565 let porcelain_type = CapabilityTypeName::from(offer);
1566 if target_dict.get_capability(&source_path).is_some()
1567 && porcelain_type != CapabilityTypeName::Directory
1568 {
1569 warn!(
1570 "duplicate sources for protocol {} in a dict, unable to populate dict entry",
1571 target_name
1572 );
1573 }
1574 let router: Router<T> = match offer.source() {
1575 cm_rust::OfferSource::Parent => {
1576 let err = if component.moniker() == &Moniker::root() {
1577 RoutingError::register_from_component_manager_not_found(
1578 offer.source_name().to_string(),
1579 )
1580 } else {
1581 RoutingError::offer_from_parent_not_found(
1582 &component.moniker(),
1583 source_path.iter_segments().join("/"),
1584 )
1585 };
1586 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1587 }
1588 cm_rust::OfferSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1589 &source_path,
1590 RoutingError::offer_from_self_not_found(
1591 &component.moniker(),
1592 source_path.iter_segments().join("/"),
1593 ),
1594 ),
1595 cm_rust::OfferSource::Child(child_ref) => {
1596 let child_name: ChildName = child_ref.clone().try_into().expect("invalid child ref");
1597 match child_component_output_dictionary_routers.get(&child_name) {
1598 None => Router::<T>::new_error(RoutingError::offer_from_child_instance_not_found(
1599 &child_name,
1600 &component.moniker(),
1601 source_path.iter_segments().join("/"),
1602 )),
1603 Some(child_component_output) => child_component_output.clone().lazy_get(
1604 source_path.to_owned(),
1605 RoutingError::offer_from_child_expose_not_found(
1606 &child_name,
1607 &component.moniker(),
1608 offer.source_name().clone(),
1609 ),
1610 ),
1611 }
1612 }
1613 cm_rust::OfferSource::Framework => {
1614 if offer.is_from_dictionary() {
1615 warn!(
1616 "routing from framework with dictionary path is not supported: {source_path}"
1617 );
1618 return;
1619 }
1620 query_framework_router_or_not_found(framework_router, &source_path, component)
1621 }
1622 cm_rust::OfferSource::Capability(capability_name) => {
1623 let err = RoutingError::capability_from_capability_not_found(
1624 &component.moniker(),
1625 capability_name.as_str().to_string(),
1626 );
1627 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1628 || source_path.iter_segments().join("/")
1629 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1630 {
1631 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1632 } else {
1633 Router::<T>::new_error(err)
1634 }
1635 }
1636 cm_rust::OfferSource::Void => UnavailableRouter::new_from_offer(offer, component),
1637 cm_rust::OfferSource::Collection(_collection_name) => {
1638 return;
1643 }
1644 };
1645
1646 let availability = *offer.availability();
1647 let mut router_builder = router
1648 .with_porcelain_with_default(porcelain_type)
1649 .availability(availability)
1650 .target(component)
1651 .error_info(offer)
1652 .error_reporter(error_reporter);
1653 if let cm_rust::OfferDecl::Directory(decl) = offer {
1654 router_builder = router_builder
1664 .rights(decl.rights.clone().map(Into::into))
1665 .subdir(decl.subdir.clone().into())
1666 .inherit_rights(true);
1667 }
1668 if let cm_rust::OfferDecl::Service(_) = offer {
1669 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1670 }
1671 if let cm_rust::OfferDecl::EventStream(offer_event_stream) = offer {
1672 if let Some(scope) = &offer_event_stream.scope {
1673 router_builder =
1674 router_builder.event_stream_route_metadata(finternal::EventStreamRouteMetadata {
1675 scope_moniker: Some(component.moniker().to_string()),
1676 scope: Some(scope.clone().native_into_fidl()),
1677 ..Default::default()
1678 });
1679 }
1680 }
1681 let router = router_builder.build().with_service_renames_and_filter(offer.clone());
1682 match target_dict.insert_capability(target_name, router.into()) {
1683 Ok(()) => (),
1684 Err(e) => warn!("failed to insert {target_name} into target dict: {e:?}"),
1685 }
1686}
1687
1688fn query_framework_router_or_not_found<T, C>(
1689 router: &Router<Dict>,
1690 path: &BorrowedSeparatedPath<'_>,
1691 component: &Arc<C>,
1692) -> Router<T>
1693where
1694 T: CapabilityBound,
1695 Router<T>: TryFrom<Capability>,
1696 C: ComponentInstanceInterface + 'static,
1697{
1698 let request = Request { metadata: Dict::new() };
1699 let dict: Result<RouterResponse<Dict>, RouterError> = router
1700 .route(Some(request), false, component.as_weak().into())
1701 .now_or_never()
1702 .expect("failed to now_or_never");
1703 let dict = match dict {
1704 Ok(RouterResponse::Capability(dict)) => dict,
1705 _ => Dict::new(),
1707 };
1708 dict.get_router_or_not_found::<T>(
1711 path,
1712 RoutingError::capability_from_framework_not_found(
1713 &component.moniker(),
1714 path.iter_segments().join("/"),
1715 ),
1716 )
1717}
1718
1719pub fn is_supported_expose(expose: &cm_rust::ExposeDecl) -> bool {
1720 matches!(
1721 expose,
1722 cm_rust::ExposeDecl::Config(_)
1723 | cm_rust::ExposeDecl::Protocol(_)
1724 | cm_rust::ExposeDecl::Dictionary(_)
1725 | cm_rust::ExposeDecl::Directory(_)
1726 | cm_rust::ExposeDecl::Runner(_)
1727 | cm_rust::ExposeDecl::Resolver(_)
1728 | cm_rust::ExposeDecl::Service(_)
1729 )
1730}
1731
1732fn extend_dict_with_expose<T, C: ComponentInstanceInterface + 'static>(
1733 component: &Arc<C>,
1734 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dict>>,
1735 program_output_dict: &Dict,
1736 framework_router: &Router<Dict>,
1737 capability_sourced_capabilities_dict: &Dict,
1738 expose: &cm_rust::ExposeDecl,
1739 target_component_output: &ComponentOutput,
1740 error_reporter: impl ErrorReporter,
1741) where
1742 T: CapabilityBound + Clone,
1743 Router<T>: TryFrom<Capability> + Into<Capability>,
1744{
1745 assert!(is_supported_expose(expose), "{expose:?}");
1746
1747 let target_dict = match expose.target() {
1748 cm_rust::ExposeTarget::Parent => target_component_output.capabilities(),
1749 cm_rust::ExposeTarget::Framework => target_component_output.framework(),
1750 };
1751 let source_path = expose.source_path();
1752 let target_name = expose.target_name();
1753
1754 let porcelain_type = CapabilityTypeName::from(expose);
1755 let router: Router<T> = match expose.source() {
1756 cm_rust::ExposeSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1757 &source_path,
1758 RoutingError::expose_from_self_not_found(
1759 &component.moniker(),
1760 source_path.iter_segments().join("/"),
1761 ),
1762 ),
1763 cm_rust::ExposeSource::Child(child_name) => {
1764 let child_name = ChildName::parse(child_name).expect("invalid static child name");
1765 if let Some(child_component_output) =
1766 child_component_output_dictionary_routers.get(&child_name)
1767 {
1768 child_component_output.clone().lazy_get(
1769 source_path.to_owned(),
1770 RoutingError::expose_from_child_expose_not_found(
1771 &child_name,
1772 &component.moniker(),
1773 expose.source_name().clone(),
1774 ),
1775 )
1776 } else {
1777 Router::<T>::new_error(RoutingError::expose_from_child_instance_not_found(
1778 &child_name,
1779 &component.moniker(),
1780 expose.source_name().clone(),
1781 ))
1782 }
1783 }
1784 cm_rust::ExposeSource::Framework => {
1785 if expose.is_from_dictionary() {
1786 warn!(
1787 "routing from framework with dictionary path is not supported: {source_path}"
1788 );
1789 return;
1790 }
1791 query_framework_router_or_not_found(framework_router, &source_path, component)
1792 }
1793 cm_rust::ExposeSource::Capability(capability_name) => {
1794 let err = RoutingError::capability_from_capability_not_found(
1795 &component.moniker(),
1796 capability_name.as_str().to_string(),
1797 );
1798 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1799 || source_path.iter_segments().join("/")
1800 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1801 {
1802 capability_sourced_capabilities_dict
1803 .clone()
1804 .get_router_or_not_found::<T>(&capability_name, err)
1805 } else {
1806 Router::<T>::new_error(err)
1807 }
1808 }
1809 cm_rust::ExposeSource::Void => UnavailableRouter::new_from_expose(expose, component),
1810 cm_rust::ExposeSource::Collection(_name) => return,
1815 };
1816 let availability = *expose.availability();
1817 let mut router_builder = router
1818 .with_porcelain_with_default(porcelain_type)
1819 .availability(availability)
1820 .target(component)
1821 .error_info(expose)
1822 .error_reporter(error_reporter);
1823 if let cm_rust::ExposeDecl::Directory(decl) = expose {
1824 router_builder = router_builder
1825 .rights(decl.rights.clone().map(Into::into))
1826 .subdir(decl.subdir.clone().into())
1827 .inherit_rights(true);
1828 };
1829 if let cm_rust::ExposeDecl::Service(_) = expose {
1830 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1831 };
1832 match target_dict.insert_capability(target_name, router_builder.build().into()) {
1833 Ok(()) => (),
1834 Err(e) => warn!("failed to insert {target_name} into target_dict: {e:?}"),
1835 }
1836}
1837
1838struct UnavailableRouter<C: ComponentInstanceInterface> {
1839 capability: InternalCapability,
1840 component: WeakComponentInstanceInterface<C>,
1841}
1842
1843impl<C: ComponentInstanceInterface + 'static> UnavailableRouter<C> {
1844 fn new<T: CapabilityBound>(capability: InternalCapability, component: &Arc<C>) -> Router<T> {
1845 Router::<T>::new(Self { capability, component: component.as_weak() })
1846 }
1847
1848 fn new_from_offer<T: CapabilityBound>(offer: &OfferDecl, component: &Arc<C>) -> Router<T> {
1849 let name = offer.source_name().clone();
1850 let capability = match offer {
1851 OfferDecl::Service(_) => InternalCapability::Service(name),
1852 OfferDecl::Protocol(_) => InternalCapability::Protocol(name),
1853 OfferDecl::Directory(_) => InternalCapability::Directory(name),
1854 OfferDecl::Storage(_) => InternalCapability::Storage(name),
1855 OfferDecl::Runner(_) => InternalCapability::Runner(name),
1856 OfferDecl::Resolver(_) => InternalCapability::Resolver(name),
1857 OfferDecl::EventStream(_) => InternalCapability::EventStream(name),
1858 OfferDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1859 OfferDecl::Config(_) => InternalCapability::Config(name),
1860 };
1861 Self::new(capability, component)
1862 }
1863
1864 fn new_from_expose<T: CapabilityBound>(expose: &ExposeDecl, component: &Arc<C>) -> Router<T> {
1865 let name = expose.source_name().clone();
1866 let capability = match expose {
1867 ExposeDecl::Service(_) => InternalCapability::Service(name),
1868 ExposeDecl::Protocol(_) => InternalCapability::Protocol(name),
1869 ExposeDecl::Directory(_) => InternalCapability::Directory(name),
1870 ExposeDecl::Runner(_) => InternalCapability::Runner(name),
1871 ExposeDecl::Resolver(_) => InternalCapability::Resolver(name),
1872 ExposeDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1873 ExposeDecl::Config(_) => InternalCapability::Config(name),
1874 };
1875 Self::new(capability, component)
1876 }
1877}
1878
1879#[async_trait]
1880impl<T: CapabilityBound, C: ComponentInstanceInterface + 'static> Routable<T>
1881 for UnavailableRouter<C>
1882{
1883 async fn route(
1884 &self,
1885 request: Option<Request>,
1886 debug: bool,
1887 _target: WeakInstanceToken,
1888 ) -> Result<RouterResponse<T>, RouterError> {
1889 if debug {
1890 let data = CapabilitySource::Void(VoidSource {
1891 capability: self.capability.clone(),
1892 moniker: self.component.moniker.clone(),
1893 })
1894 .try_into()
1895 .expect("failed to convert capability source to Data");
1896 return Ok(RouterResponse::<T>::Debug(data));
1897 }
1898 let request = request.ok_or_else(|| RouterError::InvalidArgs)?;
1899 let availability = request.metadata.get_metadata().ok_or(RouterError::InvalidArgs)?;
1900 match availability {
1901 cm_rust::Availability::Required | cm_rust::Availability::SameAsTarget => {
1902 Err(RoutingError::SourceCapabilityIsVoid {
1903 moniker: self.component.moniker.clone(),
1904 }
1905 .into())
1906 }
1907 cm_rust::Availability::Optional | cm_rust::Availability::Transitional => {
1908 Ok(RouterResponse::Unavailable)
1909 }
1910 }
1911 }
1912}