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