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